Skip to content

Commit

Permalink
Added is_subcommand_used helper function
Browse files Browse the repository at this point in the history
  • Loading branch information
p-ranav committed Sep 21, 2022
1 parent e35ce54 commit 793fbcd
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 53 deletions.
91 changes: 53 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ int main(int argc, char *argv[]) {
And running the code:
```bash
$ ./main 15
```console
foo@bar:/home/dev/$ ./main 15
225
```

Expand Down Expand Up @@ -141,8 +141,8 @@ if (program["--verbose"] == true) {
}
```
```bash
$ ./main --verbose
```console
foo@bar:/home/dev/$ ./main --verbose
Verbosity enabled
```

Expand Down Expand Up @@ -276,8 +276,8 @@ catch (const std::runtime_error& err) {
// Some code to print arguments
```

```bash
$ ./main -5 -1.1 -3.1415 -3.1e2 -4.51329E3
```console
foo@bar:/home/dev/$ ./main -5 -1.1 -3.1415 -3.1e2 -4.51329E3
integer : -5
floats : -1.1 -3.1415 -310 -4513.29
```
Expand Down Expand Up @@ -316,14 +316,14 @@ else {
}
```
```bash
$ ./main 4
```console
foo@bar:/home/dev/$ ./main 4
16
$ ./main 4 --verbose
foo@bar:/home/dev/$ ./main 4 --verbose
The square of 4 is 16
$ ./main --verbose 4
foo@bar:/home/dev/$ ./main --verbose 4
The square of 4 is 16
```

Expand All @@ -332,7 +332,7 @@ The square of 4 is 16
`std::cout << program` prints a help message, including the program usage and information about the arguments registered with the `ArgumentParser`. For the previous example, here's the default help message:

```
$ ./main --help
foo@bar:/home/dev/$ ./main --help
Usage: main [options] square
Positional arguments:
Expand Down Expand Up @@ -364,8 +364,8 @@ program.parse_args(argc, argv);
std::cout << program << std::endl;
```
```bash
$ ./main --help
```console
foo@bar:/home/dev/$ ./main --help
Usage: main thing
Forward a thing to the next member.
Expand Down Expand Up @@ -491,13 +491,13 @@ auto c = program.get<std::vector<float>>("-c"); // {1.95, 2.47}
/// Some code that prints parsed arguments
```
```bash
$ ./main -ac 3.14 2.718
```console
foo@bar:/home/dev/$ ./main -ac 3.14 2.718
a = true
b = false
c = {3.14, 2.718}
$ ./main -cb
foo@bar:/home/dev/$ ./main -cb
a = false
b = true
c = {0.0, 0.0}
Expand Down Expand Up @@ -568,8 +568,8 @@ The default is `default_arguments::all` for included arguments. No default argum
`argparse` supports gathering "remaining" arguments at the end of the command, e.g., for use in a compiler:
```bash
$ compiler file1 file2 file3
```console
foo@bar:/home/dev/$ compiler file1 file2 file3
```

To enable this, simply create an argument and mark it as `remaining`. All remaining arguments passed to argparse are gathered here.
Expand Down Expand Up @@ -601,15 +601,15 @@ try {
When no arguments are provided:
```bash
$ ./compiler
```console
foo@bar:/home/dev/$ ./compiler
No files provided
```

and when multiple arguments are provided:

```bash
$ ./compiler foo.txt bar.txt baz.txt
```console
foo@bar:/home/dev/$ ./compiler foo.txt bar.txt baz.txt
3 files provided
foo.txt
bar.txt
Expand Down Expand Up @@ -650,8 +650,8 @@ try {

```
```bash
$ ./compiler -o main foo.cpp bar.cpp baz.cpp
```console
foo@bar:/home/dev/$ ./compiler -o main foo.cpp bar.cpp baz.cpp
Output filename: main
3 files provided
foo.cpp
Expand All @@ -661,8 +661,8 @@ baz.cpp

***NOTE***: Remember to place all optional arguments BEFORE the remaining argument. If the optional argument is placed after the remaining arguments, it too will be deemed remaining:

```bash
$ ./compiler foo.cpp bar.cpp baz.cpp -o main
```console
foo@bar:/home/dev/$ ./compiler foo.cpp bar.cpp baz.cpp -o main
5 arguments provided
foo.cpp
bar.cpp
Expand Down Expand Up @@ -703,11 +703,13 @@ Many programs split up their functionality into a number of sub-commands, for ex
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("git");
// git add subparser
argparse::ArgumentParser add_command("add");
add_command.add_argument("files")
.help("Files to add content from. Fileglobs (e.g. *.c) can be given to add all matching files.")
.remaining();
// git commit subparser
argparse::ArgumentParser commit_command("commit");
commit_command.add_argument("-a", "--all")
.help("Tell the command to automatically stage files that have been modified and deleted.")
Expand All @@ -717,13 +719,15 @@ int main(int argc, char *argv[]) {
commit_command.add_argument("-m", "--message")
.help("Use the given <msg> as the commit message.");
// git cat-file subparser
argparse::ArgumentParser catfile_command("cat-file");
catfile_command.add_argument("-t")
.help("Instead of the content, show the object type identified by <object>.");
catfile_command.add_argument("-p")
.help("Pretty-print the contents of <object> based on its type.");
// git submodule subparser
argparse::ArgumentParser submodule_command("submodule");
argparse::ArgumentParser submodule_update_command("update");
submodule_update_command.add_argument("--init")
Expand Down Expand Up @@ -752,38 +756,49 @@ int main(int argc, char *argv[]) {
}
```

```bash
$ ./git --help
```console
foo@bar:/home/dev/$ ./git --help
Usage: git [options] <command> [<args>]

Optional arguments:
-h --help shows help message and exits [default: false]
-v --version prints version information and exits [default: false]

Subcommands:
{add, cat-file, commit, submodule}
add Add file contents to the index
cat-file Provide content or type and size information for repository objects
commit Record changes to the repository
submodule Initialize, update or inspect submodules

$ ./git add --help
foo@bar:/home/dev/$ ./git add --help
Usage: git add [options] files

Add file contents to the index

Positional arguments:
files Files to add content from. Fileglobs (e.g. *.c) can be given to add all matching files.

Optional arguments:
-h --help shows help message and exits [default: false]
-v --version prints version information and exits [default: false]

$ ./git submodule update --help
Usage: submodule update [options]
foo@bar:/home/dev/$ ./git submodule --help
Usage: git submodule [options] <command> [<args>]

Initialize, update or inspect submodules

Optional arguments:
-h --help shows help message and exits [default: false]
-v --version prints version information and exits [default: false]
--init [default: false]
--recursive [default: false]

Subcommands:
update Update the registered submodules to match what the superproject expects
```

When a help message is requested from a subparser, only the help for that particular parser will be printed. The help message will not include parent parser or sibling parser messages.

Additionally, every parser has a `.is_subcommand_used("<command_name>")` member function to check if a subcommand was used.

## Further Examples

### Construct a JSON object from a filename argument
Expand Down Expand Up @@ -854,8 +869,8 @@ auto files = program.get<std::vector<std::string>>("--files"); // {"a.txt", "b.
/// Some code that prints parsed arguments
```

```bash
$ ./main 1 2 3 -abc 3.14 2.718 --files a.txt b.txt c.txt
```console
foo@bar:/home/dev/$ ./main 1 2 3 -abc 3.14 2.718 --files a.txt b.txt c.txt
numbers = {1, 2, 3}
a = true
b = true
Expand Down Expand Up @@ -891,8 +906,8 @@ auto input = program.get("input");
std::cout << input << std::endl;
```
```bash
$ ./main fex
```console
foo@bar:/home/dev/$ ./main fex
baz
```

Expand Down
33 changes: 18 additions & 15 deletions include/argparse/argparse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,7 @@ class ArgumentParser {
std::string version = "1.0",
default_arguments add_args = default_arguments::all)
: m_program_name(std::move(program_name)), m_version(std::move(version)),
m_parser_path(m_program_name) {
m_parser_path(m_program_name) {
if ((add_args & default_arguments::help) == default_arguments::help) {
add_argument("-h", "--help")
.action([&](const auto & /*unused*/) {
Expand Down Expand Up @@ -959,8 +959,7 @@ class ArgumentParser {
m_is_parsed(other.m_is_parsed),
m_positional_arguments(other.m_positional_arguments),
m_optional_arguments(other.m_optional_arguments),
m_parser_path(other.m_parser_path),
m_subparsers(other.m_subparsers) {
m_parser_path(other.m_parser_path), m_subparsers(other.m_subparsers) {
for (auto it = std::begin(m_positional_arguments);
it != std::end(m_positional_arguments); ++it) {
index_argument(it);
Expand All @@ -969,9 +968,10 @@ class ArgumentParser {
it != std::end(m_optional_arguments); ++it) {
index_argument(it);
}
for (auto it = std::begin(m_subparsers);
it != std::end(m_subparsers); ++it) {
for (auto it = std::begin(m_subparsers); it != std::end(m_subparsers);
++it) {
m_subparser_map.insert_or_assign(it->get().m_program_name, it);
m_subparser_used.insert_or_assign(it->get().m_program_name, false);
}
}

Expand Down Expand Up @@ -1082,6 +1082,13 @@ class ArgumentParser {
return (*this)[arg_name].m_is_used;
}

/* Getter that returns true for user-supplied options. Returns false if not
* user-supplied, even with a default value.
*/
auto is_subcommand_used(std::string_view subcommand_name) const {
return m_subparser_used.at(subcommand_name);
}

/* Indexing operator. Return a reference to an Argument object
* Used in conjuction with Argument.operator== e.g., parser["foo"] == true
* @throws std::logic_error in case of an invalid argument name
Expand Down Expand Up @@ -1153,17 +1160,10 @@ class ArgumentParser {
? (parser.m_optional_arguments.empty() ? "" : "\n")
: "\n")
<< "Subcommands:\n";
stream << "{";
std::size_t i = 0;
for (const auto &[argument, unused] : parser.m_subparser_map) {
if (i == 0) {
stream << argument;
} else {
stream << ", " << argument;
}
++i;
for (const auto &[command, subparser] : parser.m_subparser_map) {
stream.width(longest_arg_length);
stream << command << "\t" << subparser->get().m_description << "\n";
}
stream << "}\n";
}

if (!parser.m_epilog.empty()) {
Expand Down Expand Up @@ -1194,6 +1194,7 @@ class ArgumentParser {
parser.m_parser_path = m_program_name + " " + parser.m_program_name;
auto it = m_subparsers.emplace(std::cend(m_subparsers), parser);
m_subparser_map.insert_or_assign(parser.m_program_name, it);
m_subparser_used.insert_or_assign(parser.m_program_name, false);
}

private:
Expand Down Expand Up @@ -1223,6 +1224,7 @@ class ArgumentParser {

// invoke subparser
m_is_parsed = true;
m_subparser_used[maybe_command] = true;
return subparser_it->second->get().parse_args(
unprocessed_arguments);
}
Expand Down Expand Up @@ -1296,6 +1298,7 @@ class ArgumentParser {
std::string m_parser_path;
std::list<std::reference_wrapper<ArgumentParser>> m_subparsers;
std::map<std::string_view, argument_parser_it, std::less<>> m_subparser_map;
std::map<std::string_view, bool, std::less<>> m_subparser_used;
};

} // namespace argparse
Loading

0 comments on commit 793fbcd

Please sign in to comment.