Initial commit
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
# -*- yaml -*-
|
||||
|
||||
---
|
||||
BasedOnStyle: WebKit
|
||||
IndentWidth: 4
|
||||
---
|
||||
Language: Cpp
|
||||
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
|
||||
BraceWrapping:
|
||||
AfterEnum: false
|
||||
AfterFunction: true
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
BeforeLambdaBody: false
|
||||
SplitEmptyRecord: false
|
||||
BreakBeforeBraces: Custom
|
||||
BreakInheritanceList: BeforeComma
|
||||
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceInEmptyBlock: false
|
||||
NamespaceIndentation: None
|
||||
FixNamespaceComments: true
|
||||
Standard: c++17
|
||||
TabWidth: 4
|
||||
UseTab: AlignWithSpaces
|
||||
...
|
||||
@@ -0,0 +1,7 @@
|
||||
# Directories
|
||||
|
||||
.cache/
|
||||
.clangd/
|
||||
build/
|
||||
|
||||
# Files
|
||||
@@ -0,0 +1,74 @@
|
||||
# User config between these lines
|
||||
# ------------------------------------------
|
||||
|
||||
# Set project name
|
||||
set(PROJECT "stowage")
|
||||
# Set debugging, ON/OFF
|
||||
set(DEBUG "ON")
|
||||
|
||||
# ------------------------------------------
|
||||
|
||||
# Add 'make run' target
|
||||
add_custom_target(run
|
||||
COMMAND ${PROJECT}
|
||||
)
|
||||
|
||||
# ------------------------------------------
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# Check if the build should include debugging symbols
|
||||
option(DEBUG "" ${DEBUG})
|
||||
if(DEBUG)
|
||||
# cmake -DDEBUG=on .. && make
|
||||
message("--- Debug ---")
|
||||
set(CMAKE_BUILD_TYPE "Debug")
|
||||
|
||||
# -Og = Optimizations that do not interfere with debugging
|
||||
# -Wall = All warnings about contructions that are easily avoidable
|
||||
# -Wextra = Extra warning flags not covered by -Wall
|
||||
# -g = Produce debugging information in OS's native format
|
||||
# -pg = Generate profile information for analysis with gprof
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og -Wall -Wextra -g -pg")
|
||||
# gprof <PROJECT> gmon.out > profile-data.txt
|
||||
else()
|
||||
# cmake -DDEBUG=off .. && make
|
||||
message("--- Release ---")
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
|
||||
# -O3 = Optimizations that increases compilation time and performance
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
||||
endif()
|
||||
|
||||
# Include all headers
|
||||
include_directories(
|
||||
"src"
|
||||
"test"
|
||||
)
|
||||
|
||||
# Define source files
|
||||
file(GLOB_RECURSE PROJECT_SOURCES "src/*.cpp")
|
||||
set(PROJECT_SOURCES ${PROJECT_SOURCES})
|
||||
|
||||
# Define test source files
|
||||
file(GLOB_RECURSE TEST_SOURCES "test/*.cpp")
|
||||
file(GLOB_RECURSE MAIN_SOURCES "src/*/*.cpp")
|
||||
set(TEST_SOURCES ${TEST_SOURCES} ${MAIN_SOURCES})
|
||||
|
||||
# ------------------------------------------
|
||||
|
||||
project(${PROJECT})
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(${PROJECT} ${PROJECT_SOURCES})
|
||||
target_link_libraries(${PROJECT})
|
||||
|
||||
# ------------------------------------------
|
||||
|
||||
project(test)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(test ${TEST_SOURCES})
|
||||
target_link_libraries(test)
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
* Stowage
|
||||
|
||||
Needed information for generation:
|
||||
- short option
|
||||
- long option
|
||||
- short description (for usage ouput)
|
||||
- long description (for manpage output)
|
||||
|
||||
* Option Parsing
|
||||
|
||||
Parsed from left to right.
|
||||
|
||||
** Types
|
||||
|
||||
- Short option
|
||||
- Long option
|
||||
- Argument to option
|
||||
- Non-option parameter (default argument)
|
||||
|
||||
*** Short Option
|
||||
|
||||
'-' followed by short option character.
|
||||
If option has required argument, directly after OR separated by white-space.
|
||||
Optional argument must be directly after the option character.
|
||||
|
||||
Possible to write several short options after one '-',
|
||||
if all (except last) do not have *required* or *optional* arguments.
|
||||
|
||||
*** Long Option
|
||||
|
||||
'--' followed by long option name.
|
||||
If option has a required argument, directly after, separated by '=' OR white-space.
|
||||
Optional argument must be directly after the option, separated by '='.
|
||||
|
||||
*** Non-option Parameter
|
||||
|
||||
Each parameter not starting with '-' and not a required argument of a previous option,
|
||||
is a non-option parameter.
|
||||
|
||||
Each parameter after a '--' parameter is always interpreted as a non-option parameter.
|
||||
|
||||
* Examples
|
||||
|
||||
#+BEGIN_SRC shell-script
|
||||
./stowage -Fals
|
||||
./stowage -F -a -l -s
|
||||
|
||||
./stowage --file --add --pull --push
|
||||
|
||||
./stowage -r filename
|
||||
./stowage -rfilename
|
||||
./stowage --remove=filename
|
||||
./stowage --remove filename
|
||||
|
||||
./stowage --remove filename other stuff
|
||||
./stowage --remove filename -- other stuff
|
||||
#+END_SRC
|
||||
|
||||
** Multiple of the same options
|
||||
|
||||
#+BEGIN_SRC shell-script
|
||||
./stowage -e pattern1 -epattern2 --regexp=pattern3 --regexp pattern4
|
||||
#+END_SRC
|
||||
|
||||
* TODO
|
||||
|
||||
- after first non option, go into no-option mode
|
||||
- support '--' to go into no-option mode
|
||||
- add multi-option support, vectors!
|
||||
- support argument parsing + storing
|
||||
- add addOption overloads
|
||||
- generate usage string
|
||||
- generate man page string
|
||||
- parse() function to return bool true if any error has occured
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
build/compile_commands.json
|
||||
@@ -0,0 +1,39 @@
|
||||
// #include <cstddef> // size_t
|
||||
// #include <cstdlib> // maloc, free
|
||||
#include <string>
|
||||
|
||||
#include "util/argparser.h"
|
||||
|
||||
// void* operator new(size_t size)
|
||||
// {
|
||||
// std::cout << "Allocating '" << size << "' bytes" << std::endl;
|
||||
// return std::malloc(size);
|
||||
// }
|
||||
|
||||
// void operator delete(void* pointer, size_t size)
|
||||
// {
|
||||
// std::cout << "Freeing '" << size << "' bytes" << std::endl;
|
||||
// free(pointer);
|
||||
// }
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
bool pattern = false;
|
||||
std::string stringArg1 = "default value";
|
||||
std::string stringArg2 = "nothing";
|
||||
|
||||
Util::ArgParser parser;
|
||||
// parser.setExitOnFirstError(false);
|
||||
// parser.setErrorMessages(false);
|
||||
|
||||
parser.addOption(pattern, 'e', "regexp", "search pattern", "Use ${U}PATTERNS${N} as the patterns.");
|
||||
parser.addOption(stringArg1, 'a', "arg1", "test argument", "Test argument manpage description.", "TEST", Util::ArgParser::Required::Yes);
|
||||
parser.addOption(stringArg2, 'b', "arg2", "optional argument", "Option with optional argument", "TEST", Util::ArgParser::Required::Optional);
|
||||
parser.parse(argc, argv);
|
||||
|
||||
printf(" Pattern: {%d}\n", pattern);
|
||||
printf(" Arg1: {%s}\n", stringArg1.data());
|
||||
printf(" Arg2: {%s}\n", stringArg2.data());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
#include <algorithm> // std::find_if
|
||||
#include <cstdio> // printf
|
||||
#include <string_view>
|
||||
|
||||
#include "util/argparser.h"
|
||||
|
||||
namespace Util {
|
||||
|
||||
ArgParser::ArgParser()
|
||||
{
|
||||
}
|
||||
|
||||
ArgParser::~ArgParser()
|
||||
{
|
||||
}
|
||||
|
||||
void ArgParser::printOptionError(char name, Error error)
|
||||
{
|
||||
char tmp[] { name, '\0' };
|
||||
printOptionError(tmp, error, false);
|
||||
}
|
||||
|
||||
void ArgParser::printOptionError(const char* name, Error error, bool longName)
|
||||
{
|
||||
if (!m_errorMessages) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (error == Error::InvalidOption) {
|
||||
printf("%s: invalid option -- '%s'\n", m_name, name);
|
||||
}
|
||||
else if (error == Error::UnrecognizedOption) {
|
||||
printf("%s: unrecognized option -- '%s'\n", m_name, name);
|
||||
}
|
||||
else if (error == Error::DoesntAllowArgument) {
|
||||
printf("%s: option '--%s' doesn't allow an argument\n", m_name, name);
|
||||
}
|
||||
else if (error == Error::RequiresArgument) {
|
||||
if (longName) {
|
||||
printf("%s: option '--%s' requires an argument", m_name, name);
|
||||
}
|
||||
else {
|
||||
printf("%s: option requires an argument -- '%s'\n", m_name, name);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Print command usage, if it's enabled.
|
||||
}
|
||||
|
||||
// Required: directly after || separated by space
|
||||
// Optional: directly after
|
||||
bool ArgParser::parseShortOption(std::string_view option, std::string_view next)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
printf("Parsing short option: '%s'\n", option.data());
|
||||
|
||||
char c;
|
||||
std::string_view value;
|
||||
for (std::string_view::size_type i = 0; i < option.size(); ++i) {
|
||||
c = option.at(i);
|
||||
printf("short '%c'\n", c);
|
||||
|
||||
auto foundOption = std::find_if(m_options.begin(), m_options.end(), [&c](Option& it) -> bool {
|
||||
return it.shortName == c;
|
||||
});
|
||||
|
||||
// Option does not exist
|
||||
if (foundOption == m_options.cend()) {
|
||||
printOptionError(c, Error::InvalidOption);
|
||||
|
||||
result = false;
|
||||
if (m_exitOnFirstError) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundOption->requiresArgument == Required::No) {
|
||||
// FIXME: Figure out why providing a nullptr breaks the lambda here.
|
||||
foundOption->acceptValue("");
|
||||
}
|
||||
else if (foundOption->requiresArgument == Required::Yes) {
|
||||
value = option.substr(i + 1);
|
||||
if (value.empty() && next.empty()) {
|
||||
foundOption->error = Error::RequiresArgument;
|
||||
printOptionError(c, Error::RequiresArgument);
|
||||
|
||||
result = false;
|
||||
if (m_exitOnFirstError) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else if (!value.empty()) {
|
||||
foundOption->acceptValue(value.data());
|
||||
}
|
||||
else if (next[0] == '-') {
|
||||
foundOption->error = Error::RequiresArgument;
|
||||
printOptionError(c, Error::RequiresArgument);
|
||||
|
||||
result = false;
|
||||
if (m_exitOnFirstError) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
foundOption->acceptValue(next.data());
|
||||
m_optionIndex++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else if (foundOption->requiresArgument == Required::Optional) {
|
||||
value = option.substr(i + 1);
|
||||
if (!value.empty()) {
|
||||
foundOption->acceptValue(value.data());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Required: directly after, separated by '=' || separated by space
|
||||
// Optional: directly after, separated by '='
|
||||
bool ArgParser::parseLongOption(std::string_view option, std::string_view next)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
std::string_view name = option.substr(0, option.find_first_of('='));
|
||||
std::string_view value = option.substr(option.find_first_of('=') + 1);
|
||||
|
||||
bool argumentProvided = true;
|
||||
if (name.compare(value) == 0 && option.find('=') == std::string_view::npos) {
|
||||
argumentProvided = false;
|
||||
}
|
||||
|
||||
printf("Parsing long option: '%s' with value '%s'\n", name.data(), argumentProvided ? value.data() : "");
|
||||
|
||||
auto foundOption = std::find_if(m_options.begin(), m_options.end(), [&name](Option& it) -> bool {
|
||||
return it.longName == name;
|
||||
});
|
||||
|
||||
if (foundOption == m_options.cend()) {
|
||||
foundOption->error = Error::UnrecognizedOption;
|
||||
printOptionError(name.data(), Error::UnrecognizedOption);
|
||||
|
||||
result = false;
|
||||
if (m_exitOnFirstError) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (argumentProvided) {
|
||||
if (foundOption->requiresArgument == Required::No) {
|
||||
foundOption->error = Error::DoesntAllowArgument;
|
||||
printOptionError(name.data(), Error::DoesntAllowArgument);
|
||||
|
||||
result = false;
|
||||
if (m_exitOnFirstError) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else if (foundOption->requiresArgument == Required::Yes) {
|
||||
foundOption->acceptValue(value.data());
|
||||
}
|
||||
else if (foundOption->requiresArgument == Required::Optional) {
|
||||
foundOption->acceptValue(value.data());
|
||||
}
|
||||
}
|
||||
else if (!next.empty() && foundOption->requiresArgument == Required::Yes) {
|
||||
if (next[0] == '-') {
|
||||
foundOption->error = Error::RequiresArgument;
|
||||
printOptionError(name.data(), Error::RequiresArgument);
|
||||
|
||||
result = false;
|
||||
if (m_exitOnFirstError) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
foundOption->acceptValue(next.data());
|
||||
m_optionIndex++;
|
||||
}
|
||||
}
|
||||
else if (foundOption->requiresArgument == Required::Yes) {
|
||||
foundOption->error = Error::RequiresArgument;
|
||||
printOptionError(name.data(), Error::RequiresArgument);
|
||||
|
||||
result = false;
|
||||
if (m_exitOnFirstError) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// FIXME: Figure out why providing a nullptr breaks the lambda here.
|
||||
foundOption->acceptValue("");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ArgParser::parse(int argc, const char* argv[])
|
||||
{
|
||||
// Get program name
|
||||
m_name = argv[0] + std::string_view(argv[0]).find_last_of('/') + 1;
|
||||
|
||||
printf("name: %s\n", m_name);
|
||||
|
||||
std::string_view argument;
|
||||
std::string_view next;
|
||||
for (; m_optionIndex < argc; ++m_optionIndex) {
|
||||
printf("argv[%d]: %s\n", m_optionIndex, argv[m_optionIndex]);
|
||||
|
||||
argument = argv[m_optionIndex];
|
||||
if (m_optionIndex + 1 < argc && argv[m_optionIndex + 1][0] != '-') {
|
||||
next = argv[m_optionIndex + 1];
|
||||
}
|
||||
else {
|
||||
next = {};
|
||||
}
|
||||
|
||||
// Long Option
|
||||
if (argument[0] == '-' && argument[1] == '-') {
|
||||
argument = argument.substr(argument.find_first_not_of('-'));
|
||||
parseLongOption(argument, next);
|
||||
}
|
||||
// Short Option
|
||||
else if (argument[0] == '-') {
|
||||
argument = argument.substr(argument.find_first_not_of('-'));
|
||||
parseShortOption(argument, next);
|
||||
}
|
||||
// Argument
|
||||
else {
|
||||
printf("-> argu: '%s'", argument.data());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ArgParser::addOption(Option&& option)
|
||||
{
|
||||
m_options.push_back(option);
|
||||
}
|
||||
|
||||
void ArgParser::addOption(bool& value, char shortName, const char* longName, const char* usageString, const char* manString)
|
||||
{
|
||||
Option option {
|
||||
shortName,
|
||||
longName,
|
||||
nullptr,
|
||||
usageString,
|
||||
manString,
|
||||
Required::No,
|
||||
[&value](const char*) -> bool {
|
||||
value = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
addOption(std::move(option));
|
||||
}
|
||||
|
||||
void ArgParser::addOption(std::string& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument)
|
||||
{
|
||||
Option option {
|
||||
shortName,
|
||||
longName,
|
||||
argumentName,
|
||||
usageString,
|
||||
manString,
|
||||
requiresArgument,
|
||||
[&value](const char* a) -> bool {
|
||||
value = a;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
addOption(std::move(option));
|
||||
}
|
||||
|
||||
} // namespace Util
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef ARG_PARSER_H
|
||||
#define ARG_PARSER_H
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace Util {
|
||||
|
||||
class ArgParser final {
|
||||
public:
|
||||
ArgParser();
|
||||
virtual ~ArgParser();
|
||||
|
||||
enum class Required {
|
||||
No,
|
||||
Yes,
|
||||
Optional,
|
||||
};
|
||||
|
||||
enum class Error {
|
||||
None,
|
||||
InvalidOption, // For short options
|
||||
UnrecognizedOption, // For long options
|
||||
DoesntAllowArgument,
|
||||
RequiresArgument,
|
||||
};
|
||||
|
||||
struct Option {
|
||||
char shortName { 0 };
|
||||
const char* longName { nullptr };
|
||||
const char* argumentName { nullptr };
|
||||
const char* usageString { nullptr };
|
||||
const char* manString { nullptr };
|
||||
Required requiresArgument;
|
||||
std::function<bool(const char*)> acceptValue;
|
||||
|
||||
Error error = Error::None;
|
||||
};
|
||||
|
||||
struct Argument {
|
||||
};
|
||||
|
||||
bool parse(int argc, const char* argv[]);
|
||||
|
||||
void addOption(Option&& option);
|
||||
void addOption(bool& value, char shortName, const char* longName, const char* usageString, const char* manString);
|
||||
void addOption(std::string& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName = "", Required requiresArgument = Required::No);
|
||||
|
||||
void setOptionIndex(int index) { m_optionIndex = index; }
|
||||
void setExitOnFirstError(bool state) { m_exitOnFirstError = state; }
|
||||
void setErrorMessages(bool state) { m_errorMessages = state; }
|
||||
void setNonOptionAfterFirst(bool state) { m_nonOptionAfterFirst = state; }
|
||||
|
||||
private:
|
||||
void printOptionError(char name, Error error);
|
||||
void printOptionError(const char* name, Error error, bool longName = true);
|
||||
bool parseShortOption(std::string_view option, std::string_view next);
|
||||
bool parseLongOption(std::string_view option, std::string_view next);
|
||||
|
||||
int m_optionIndex { 1 };
|
||||
bool m_exitOnFirstError { true };
|
||||
bool m_errorMessages { true };
|
||||
// TODO: Implement this, maybe combine with error messages flag, enum class? or bitfield
|
||||
bool m_nonOptionAfterFirst { false };
|
||||
|
||||
const char* m_name;
|
||||
std::vector<Option> m_options;
|
||||
std::vector<Argument> m_arguments;
|
||||
};
|
||||
|
||||
} // namespace Util
|
||||
|
||||
#endif // ARG_PARSER_H
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef TEST_H
|
||||
#define TEST_H
|
||||
|
||||
#include <cstdio> // printf
|
||||
#include <iostream> // cout
|
||||
|
||||
#define EXPECT(x) \
|
||||
if (!(x)) { \
|
||||
printf("FAIL: %s:%d: EXPECT(%s) failed\n", __FILE__, __LINE__, #x); \
|
||||
}
|
||||
|
||||
#define EXPECT_EQ(a, b) \
|
||||
if (a != b) { \
|
||||
std::cout << "FAIL: " << __FILE__ << ":" << __LINE__ \
|
||||
<< ": EXPECT_EQ(" << #a << ", " << #b ") failed with lhs=" << a << " and rhs=" << b << std::endl; \
|
||||
}
|
||||
|
||||
#endif // TEST_H
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
#include <cstdio> // fopen, printf, stdout
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "macro.h"
|
||||
#include "util/argparser.h"
|
||||
|
||||
FILE* output;
|
||||
FILE* null;
|
||||
|
||||
bool runParser(std::vector<const char*> arguments, std::function<void(Util::ArgParser&)> initializer = {})
|
||||
{
|
||||
stdout = null;
|
||||
|
||||
Util::ArgParser parser;
|
||||
if (initializer) {
|
||||
initializer(parser);
|
||||
}
|
||||
|
||||
arguments.insert(arguments.begin(), "app");
|
||||
auto result = parser.parse(arguments.size(), arguments.data());
|
||||
|
||||
stdout = output;
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int, const char*[])
|
||||
{
|
||||
output = stdout;
|
||||
null = fopen("/dev/null", "w"); // Windows: nul
|
||||
|
||||
printf("Test project\n");
|
||||
|
||||
// No arguments
|
||||
{
|
||||
auto result = runParser({});
|
||||
EXPECT_EQ(result, true);
|
||||
}
|
||||
|
||||
// Bool options
|
||||
{
|
||||
// Short option
|
||||
bool boolOpt1 = false;
|
||||
auto result = runParser({ "-b" }, [&](auto& parser) {
|
||||
parser.addOption(boolOpt1, 'b', nullptr, nullptr, nullptr);
|
||||
});
|
||||
EXPECT_EQ(result, true);
|
||||
EXPECT_EQ(boolOpt1, true);
|
||||
}
|
||||
{
|
||||
// Short option, not given
|
||||
bool boolOpt1 = false;
|
||||
auto result = runParser({}, [&](auto& parser) {
|
||||
parser.addOption(boolOpt1, 'b', nullptr, nullptr, nullptr);
|
||||
});
|
||||
EXPECT_EQ(result, true);
|
||||
EXPECT_EQ(boolOpt1, false);
|
||||
}
|
||||
{
|
||||
// Long option
|
||||
bool boolOpt1 = false;
|
||||
auto result = runParser({ "--bool" }, [&](auto& parser) {
|
||||
parser.addOption(boolOpt1, '\0', "bool", nullptr, nullptr);
|
||||
});
|
||||
EXPECT_EQ(result, true);
|
||||
EXPECT_EQ(boolOpt1, true);
|
||||
}
|
||||
{
|
||||
// Long option, not given
|
||||
bool boolOpt1 = false;
|
||||
auto result = runParser({}, [&](auto& parser) {
|
||||
parser.addOption(boolOpt1, '\0', "bool", nullptr, nullptr);
|
||||
});
|
||||
EXPECT_EQ(result, true);
|
||||
EXPECT_EQ(boolOpt1, false);
|
||||
}
|
||||
{
|
||||
// Allow both short and long option, provide short
|
||||
bool boolOpt1 = false;
|
||||
auto result = runParser({ "-b" }, [&](auto& parser) {
|
||||
parser.addOption(boolOpt1, 'b', "bool", nullptr, nullptr);
|
||||
});
|
||||
EXPECT_EQ(result, true);
|
||||
EXPECT_EQ(boolOpt1, true);
|
||||
}
|
||||
{
|
||||
// Allow both short and long option, provide long
|
||||
bool boolOpt1 = false;
|
||||
auto result = runParser({ "--bool" }, [&](auto& parser) {
|
||||
parser.addOption(boolOpt1, 'b', "bool", nullptr, nullptr);
|
||||
});
|
||||
EXPECT_EQ(result, true);
|
||||
EXPECT_EQ(boolOpt1, true);
|
||||
}
|
||||
{
|
||||
// Allow both short and long option, provide both
|
||||
bool boolOpt1 = false;
|
||||
auto result = runParser({ "-b", "--bool" }, [&](auto& parser) {
|
||||
parser.addOption(boolOpt1, 'b', "bool", nullptr, nullptr);
|
||||
});
|
||||
EXPECT_EQ(result, true);
|
||||
EXPECT_EQ(boolOpt1, true);
|
||||
}
|
||||
|
||||
// ..
|
||||
{
|
||||
//
|
||||
bool boolOpt1 = false;
|
||||
std::string stringOpt1 = "";
|
||||
auto result = runParser({ "-b", "something", "-s", "my-value" }, [&](auto& parser) {
|
||||
parser.addOption(boolOpt1, 'b', nullptr, nullptr, nullptr);
|
||||
parser.addOption(stringOpt1, 's', nullptr, nullptr, nullptr, nullptr, Util::ArgParser::Required::Yes);
|
||||
});
|
||||
EXPECT_EQ(result, true);
|
||||
EXPECT_EQ(boolOpt1, true);
|
||||
EXPECT_EQ(stringOpt1, "");
|
||||
}
|
||||
|
||||
// // bool tests
|
||||
// test('o', "option", { "-o" }, true);
|
||||
// test('o', "option", { "-n" }, false);
|
||||
// test('o', "option", { "--option" }, true);
|
||||
// test('o', "option", { "--noexist" }, false);
|
||||
|
||||
// // string tests
|
||||
// test('o', "option", Util::ArgParser::Required::Yes, { "-o", "my-argument" }, "my-argument", 0);
|
||||
// test('o', "option", Util::ArgParser::Required::Optional, { "-o", "my-argument" }, {}, 0);
|
||||
// test('o', "option", Util::ArgParser::Required::No, { "-o", "my-argument" }, {}, 0);
|
||||
|
||||
// test('o', "option", Util::ArgParser::Required::Yes, { "-omy-argument" }, "my-argument", 0);
|
||||
// test('o', "option", Util::ArgParser::Required::Optional, { "-omy-argument" }, "my-argument", 0);
|
||||
// test('o', "option", Util::ArgParser::Required::No, { "-omy-argument" }, {}, 0);
|
||||
|
||||
// test('o', "option", Util::ArgParser::Required::Yes, { "--option", "my-argument" }, "my-argument", 0);
|
||||
// test('o', "option", Util::ArgParser::Required::Optional, { "--option", "my-argument" }, {}, 0);
|
||||
// test('o', "option", Util::ArgParser::Required::No, { "--option", "my-argument" }, {}, 0);
|
||||
|
||||
// test('o', "option", Util::ArgParser::Required::Yes, { "--option=my-argument" }, "my-argument", 0);
|
||||
// test('o', "option", Util::ArgParser::Required::Optional, { "--option=my-argument" }, "my-argument", 0);
|
||||
// test('o', "option", Util::ArgParser::Required::No , { "--option=my-argument" }, {}, 0);
|
||||
|
||||
// test('o', "option", Util::ArgParser::Required::Yes, { "-o", "my-argument" }, "not-same", -1);
|
||||
// test('o', "option", Util::ArgParser::Required::Yes, { "-omy-argument" }, "not-same", -1);
|
||||
// test('o', "option", Util::ArgParser::Required::Optional, { "-omy-argument" }, "not-same", -1);
|
||||
|
||||
// test('o', "option", Util::ArgParser::Required::Yes, { "--option", "my-argument" }, "not-same", -1);
|
||||
// test('o', "option", Util::ArgParser::Required::Yes, { "--option=my-argument" }, "not-same", -1);
|
||||
// test('o', "option", Util::ArgParser::Required::Optional, { "--option=my-argument" }, "not-same", -1);
|
||||
|
||||
// ./help -o something -a my-value
|
||||
// -a has required argument, but something should stop option parsing
|
||||
|
||||
printf("Completed running tests\n");
|
||||
|
||||
fclose(null);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user