From 20095a697ae6060a02e8adb466e476bd255abf3e Mon Sep 17 00:00:00 2001 From: Pranav Srinivas Kumar Date: Wed, 21 Sep 2022 06:54:34 -0700 Subject: [PATCH] Added option=value support using #185 --- include/argparse/argparse.hpp | 37 +++++++++++++++++++++++++++++++++-- test/CMakeLists.txt | 1 + test/test_equals_form.cpp | 31 +++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 test/test_equals_form.cpp diff --git a/include/argparse/argparse.hpp b/include/argparse/argparse.hpp index f8d84f9..8235ba8 100644 --- a/include/argparse/argparse.hpp +++ b/include/argparse/argparse.hpp @@ -1226,10 +1226,42 @@ public: } private: + /* + * Pre-process this argument list. Anything starting with "--", that + * contains an =, where the prefix before the = has an entry in the + * options table, should be split. + */ + std::vector + preprocess_arguments(const std::vector &raw_arguments) { + std::vector arguments; + for (const auto &arg : raw_arguments) { + // Check that: + // - We don't have an argument named exactly this + // - The argument starts with "--" + // - The argument contains a "=" + std::size_t eqpos = arg.find("="); + if (m_argument_map.find(arg) == m_argument_map.end() && + arg.rfind("--", 0) == 0 && eqpos != std::string::npos) { + // Get the name of the potential option, and check it exists + std::string opt_name = arg.substr(0, eqpos); + if (m_argument_map.find(opt_name) != m_argument_map.end()) { + // This is the name of an option! Split it into two parts + arguments.push_back(std::move(opt_name)); + arguments.push_back(arg.substr(eqpos + 1)); + continue; + } + } + // If we've fallen through to here, then it's a standard argument + arguments.push_back(arg); + } + return arguments; + } + /* * @throws std::runtime_error in case of any invalid argument */ - void parse_args_internal(const std::vector &arguments) { + void parse_args_internal(const std::vector &raw_arguments) { + auto arguments = preprocess_arguments(raw_arguments); if (m_program_name.empty() && !arguments.empty()) { m_program_name = arguments.front(); } @@ -1294,7 +1326,8 @@ private: * Like parse_args_internal but collects unused args into a vector */ std::vector - parse_known_args_internal(const std::vector &arguments) { + parse_known_args_internal(const std::vector &raw_arguments) { + auto arguments = preprocess_arguments(raw_arguments); std::vector unknown_arguments{}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b57da7d..dd7aecc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -48,6 +48,7 @@ file(GLOB ARGPARSE_TEST_SOURCES test_version.cpp test_subparsers.cpp test_parse_known_args.cpp + test_equals_form.cpp ) set_source_files_properties(main.cpp PROPERTIES diff --git a/test/test_equals_form.cpp b/test/test_equals_form.cpp new file mode 100644 index 0000000..e09b664 --- /dev/null +++ b/test/test_equals_form.cpp @@ -0,0 +1,31 @@ +#include +#include +#include +using doctest::test_suite; + +TEST_CASE("Basic --value=value" * test_suite("equals_form")) { + argparse::ArgumentParser parser("test"); + parser.add_argument("--long"); + parser.parse_args({"test", "--long=value"}); + std::string result{parser.get("--long")}; + REQUIRE(result == "value"); +} + +TEST_CASE("Fallback = in regular option name" * test_suite("equals_form")) { + argparse::ArgumentParser parser("test"); + parser.add_argument("--long=mislead"); + parser.parse_args({"test", "--long=mislead", "value"}); + std::string result{parser.get("--long=mislead")}; + REQUIRE(result == "value"); +} + +TEST_CASE("Duplicate =-named and standard" * test_suite("equals_form")) { + argparse::ArgumentParser parser("test"); + parser.add_argument("--long=mislead"); + parser.add_argument("--long").default_value(std::string{"NO_VALUE"}); + parser.parse_args({"test", "--long=mislead", "value"}); + std::string result{parser.get("--long=mislead")}; + REQUIRE(result == "value"); + std::string result2{parser.get("--long")}; + REQUIRE(result2 == "NO_VALUE"); +} \ No newline at end of file