mirror of
https://github.com/KeqingMoe/argparse.git
synced 2025-07-04 15:14:39 +00:00
Draft implementation of subparsers #42
This commit is contained in:
parent
327e16c61b
commit
2f2858a8a7
@ -1167,6 +1167,11 @@ public:
|
|||||||
return out.str();
|
return out.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void add_subparser(ArgumentParser& parser) {
|
||||||
|
auto it = m_subparsers.emplace(std::cend(m_subparsers), parser);
|
||||||
|
m_subparser_map.insert_or_assign(parser.m_program_name, it);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
* @throws std::runtime_error in case of any invalid argument
|
* @throws std::runtime_error in case of any invalid argument
|
||||||
@ -1181,6 +1186,21 @@ private:
|
|||||||
const auto ¤t_argument = *it;
|
const auto ¤t_argument = *it;
|
||||||
if (Argument::is_positional(current_argument)) {
|
if (Argument::is_positional(current_argument)) {
|
||||||
if (positional_argument_it == std::end(m_positional_arguments)) {
|
if (positional_argument_it == std::end(m_positional_arguments)) {
|
||||||
|
|
||||||
|
std::string_view maybe_command = current_argument;
|
||||||
|
|
||||||
|
// Check sub-parsers
|
||||||
|
auto subparser_it = m_subparser_map.find(maybe_command);
|
||||||
|
if (subparser_it != m_subparser_map.end()) {
|
||||||
|
|
||||||
|
// build list of remaining args
|
||||||
|
const auto unprocessed_arguments = std::vector<std::string>(it, end);
|
||||||
|
|
||||||
|
// invoke subparser
|
||||||
|
m_is_parsed = true;
|
||||||
|
return subparser_it->second->get().parse_args(unprocessed_arguments);
|
||||||
|
}
|
||||||
|
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Maximum number of positional arguments exceeded");
|
"Maximum number of positional arguments exceeded");
|
||||||
}
|
}
|
||||||
@ -1226,9 +1246,10 @@ private:
|
|||||||
return max_size;
|
return max_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
using list_iterator = std::list<Argument>::iterator;
|
using argument_it = std::list<Argument>::iterator;
|
||||||
|
using argument_parser_it = std::list<std::reference_wrapper<ArgumentParser>>::iterator;
|
||||||
|
|
||||||
void index_argument(list_iterator it) {
|
void index_argument(argument_it it) {
|
||||||
for (const auto &name : std::as_const(it->m_names)) {
|
for (const auto &name : std::as_const(it->m_names)) {
|
||||||
m_argument_map.insert_or_assign(name, it);
|
m_argument_map.insert_or_assign(name, it);
|
||||||
}
|
}
|
||||||
@ -1241,7 +1262,9 @@ private:
|
|||||||
bool m_is_parsed = false;
|
bool m_is_parsed = false;
|
||||||
std::list<Argument> m_positional_arguments;
|
std::list<Argument> m_positional_arguments;
|
||||||
std::list<Argument> m_optional_arguments;
|
std::list<Argument> m_optional_arguments;
|
||||||
std::map<std::string_view, list_iterator, std::less<>> m_argument_map;
|
std::map<std::string_view, argument_it, std::less<>> m_argument_map;
|
||||||
|
std::list<std::reference_wrapper<ArgumentParser>> m_subparsers;
|
||||||
|
std::map<std::string_view, argument_parser_it, std::less<>> m_subparser_map;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace argparse
|
} // namespace argparse
|
||||||
|
@ -46,6 +46,7 @@ file(GLOB ARGPARSE_TEST_SOURCES
|
|||||||
test_scan.cpp
|
test_scan.cpp
|
||||||
test_value_semantics.cpp
|
test_value_semantics.cpp
|
||||||
test_version.cpp
|
test_version.cpp
|
||||||
|
test_subparsers.cpp
|
||||||
)
|
)
|
||||||
set_source_files_properties(main.cpp
|
set_source_files_properties(main.cpp
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
|
153
test/test_subparsers.cpp
Normal file
153
test/test_subparsers.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#include <doctest.hpp>
|
||||||
|
#include <argparse/argparse.hpp>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
using doctest::test_suite;
|
||||||
|
|
||||||
|
TEST_CASE("Add subparsers" * test_suite("subparsers")) {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
program.add_argument("--output");
|
||||||
|
|
||||||
|
argparse::ArgumentParser command_1("add");
|
||||||
|
command_1.add_argument("file").nargs(2);
|
||||||
|
|
||||||
|
argparse::ArgumentParser command_2("clean");
|
||||||
|
|
||||||
|
program.add_subparser(command_1);
|
||||||
|
program.add_subparser(command_2);
|
||||||
|
|
||||||
|
program.parse_args({ "test", "--output", "thrust_profile.csv" });
|
||||||
|
REQUIRE(program.get("--output") == "thrust_profile.csv");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse subparser command" * test_suite("subparsers")) {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
program.add_argument("--output");
|
||||||
|
|
||||||
|
argparse::ArgumentParser command_1("add");
|
||||||
|
command_1.add_argument("file").nargs(2);
|
||||||
|
|
||||||
|
argparse::ArgumentParser command_2("clean");
|
||||||
|
command_2.add_argument("--fullclean")
|
||||||
|
.default_value(false)
|
||||||
|
.implicit_value(true);
|
||||||
|
|
||||||
|
program.add_subparser(command_1);
|
||||||
|
program.add_subparser(command_2);
|
||||||
|
|
||||||
|
SUBCASE("command 1") {
|
||||||
|
program.parse_args({ "test", "add", "file1.txt", "file2.txt" });
|
||||||
|
REQUIRE(command_1.is_used("file"));
|
||||||
|
REQUIRE((command_1.get<std::vector<std::string>>("file") == std::vector<std::string>{"file1.txt", "file2.txt"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("command 2") {
|
||||||
|
program.parse_args({ "test", "clean", "--fullclean" });
|
||||||
|
REQUIRE(command_2.get<bool>("--fullclean") == true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse subparser command with optional argument" * test_suite("subparsers")) {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
program.add_argument("--verbose")
|
||||||
|
.default_value(false)
|
||||||
|
.implicit_value(true);
|
||||||
|
|
||||||
|
argparse::ArgumentParser command_1("add");
|
||||||
|
command_1.add_argument("file");
|
||||||
|
|
||||||
|
argparse::ArgumentParser command_2("clean");
|
||||||
|
command_2.add_argument("--fullclean")
|
||||||
|
.default_value(false)
|
||||||
|
.implicit_value(true);
|
||||||
|
|
||||||
|
program.add_subparser(command_1);
|
||||||
|
program.add_subparser(command_2);
|
||||||
|
|
||||||
|
SUBCASE("Optional argument BEFORE subcommand") {
|
||||||
|
program.parse_args({ "test", "--verbose", "clean", "--fullclean" });
|
||||||
|
REQUIRE(program.get<bool>("--verbose") == true);
|
||||||
|
REQUIRE(command_2.get<bool>("--fullclean") == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("Optional argument AFTER subcommand") {
|
||||||
|
REQUIRE_THROWS_WITH_AS(program.parse_args({ "test", "clean", "--fullclean", "--verbose" }),
|
||||||
|
"Unknown argument: --verbose", std::runtime_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse git commands" * test_suite("subparsers")) {
|
||||||
|
argparse::ArgumentParser program("git");
|
||||||
|
|
||||||
|
argparse::ArgumentParser add_command("add");
|
||||||
|
add_command.add_argument("files")
|
||||||
|
.remaining();
|
||||||
|
|
||||||
|
argparse::ArgumentParser commit_command("commit");
|
||||||
|
commit_command.add_argument("-a")
|
||||||
|
.default_value(false)
|
||||||
|
.implicit_value(true);
|
||||||
|
|
||||||
|
commit_command.add_argument("-m");
|
||||||
|
|
||||||
|
argparse::ArgumentParser catfile_command("cat-file");
|
||||||
|
catfile_command.add_argument("-t");
|
||||||
|
catfile_command.add_argument("-p");
|
||||||
|
|
||||||
|
argparse::ArgumentParser submodule_command("submodule");
|
||||||
|
argparse::ArgumentParser submodule_update_command("update");
|
||||||
|
submodule_update_command.add_argument("--init")
|
||||||
|
.default_value(false)
|
||||||
|
.implicit_value(true);
|
||||||
|
submodule_update_command.add_argument("--recursive")
|
||||||
|
.default_value(false)
|
||||||
|
.implicit_value(true);
|
||||||
|
submodule_command.add_subparser(submodule_update_command);
|
||||||
|
|
||||||
|
program.add_subparser(add_command);
|
||||||
|
program.add_subparser(commit_command);
|
||||||
|
program.add_subparser(catfile_command);
|
||||||
|
program.add_subparser(submodule_command);
|
||||||
|
|
||||||
|
SUBCASE("git add") {
|
||||||
|
program.parse_args({ "git", "add", "main.cpp", "foo.hpp", "foo.cpp" });
|
||||||
|
REQUIRE((add_command.get<std::vector<std::string>>("files") == std::vector<std::string>{"main.cpp", "foo.hpp", "foo.cpp"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("git commit") {
|
||||||
|
program.parse_args({ "git", "commit", "-am", "Initial commit" });
|
||||||
|
REQUIRE(commit_command.get<bool>("-a") == true);
|
||||||
|
REQUIRE(commit_command.get<std::string>("-m") == std::string{"Initial commit"});
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("git cat-file -t") {
|
||||||
|
program.parse_args({ "git", "cat-file", "-t", "3739f5" });
|
||||||
|
REQUIRE(catfile_command.get<std::string>("-t") == std::string{"3739f5"});
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("git cat-file -p") {
|
||||||
|
program.parse_args({ "git", "cat-file", "-p", "3739f5" });
|
||||||
|
REQUIRE(catfile_command.get<std::string>("-p") == std::string{"3739f5"});
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("git submodule update") {
|
||||||
|
program.parse_args({ "git", "submodule", "update" });
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("git submodule update --init") {
|
||||||
|
program.parse_args({ "git", "submodule", "update", "--init" });
|
||||||
|
REQUIRE(submodule_update_command.get<bool>("--init") == true);
|
||||||
|
REQUIRE(submodule_update_command.get<bool>("--recursive") == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("git submodule update --recursive") {
|
||||||
|
program.parse_args({ "git", "submodule", "update", "--recursive" });
|
||||||
|
REQUIRE(submodule_update_command.get<bool>("--recursive") == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("git submodule update --init --recursive") {
|
||||||
|
program.parse_args({ "git", "submodule", "update", "--init", "--recursive" });
|
||||||
|
REQUIRE(submodule_update_command.get<bool>("--init") == true);
|
||||||
|
REQUIRE(submodule_update_command.get<bool>("--recursive") == true);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user