-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1185 from smehringer/argument_parser
[FIX, FEATURE] Fix initialising the version_check_option. Add multi-level argument parsing.
- Loading branch information
Showing
14 changed files
with
411 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# How to write an argument parser with subcommands {#subcommand_arg_parse} | ||
|
||
[TOC] | ||
|
||
This HowTo shows you how to write an argument parser with subcommand like `git push` using SeqAn3. | ||
|
||
\tutorial_head{Easy, 15 min, \ref tutorial_argument_parser, } | ||
|
||
# Motivation | ||
|
||
A common use case for command line tools, e.g. `git`, is to have multiple subcommands, e.g. `git fetch` or `git push`. | ||
Each subcommand has its own set of options and its own help page. | ||
This HowTo explains how this can be done with the seqan3::argument_parser and serves as a copy'n'paste source. | ||
If you are new to SeqAn, we recommend to do the basic | ||
\link tutorial_argument_parser argument parser tutorial \endlink before you read further. | ||
|
||
# A subcommand argument parser | ||
|
||
In order to keep parsing with subcommands straightforward and simple, | ||
the seqan3::argument_parser provides an advanced interface that internally takes care of the correct input parsing. | ||
|
||
You simply need to specify the names of the subcommands when constructing your top-level argument parser: | ||
|
||
\snippet doc/howto/subcommand_argument_parser/subcommand_arg_parse.cpp construction | ||
|
||
\attention You can still add flags to your top-level parser if needed but **no (positional) options**. | ||
This avoids ambiguous parsing (e.g. subcommand fasta given file extension fasta | ||
`./myfasta_parser --filext fasta fasta ...`). | ||
|
||
After calling seqan3::argument_parser::parse() on your top-level parser, | ||
you can then access the sub-parser via the function seqan3::argument_parser::get_sub_parser(): | ||
|
||
\snippet doc/howto/subcommand_argument_parser/subcommand_arg_parse.cpp get_sub_parser | ||
|
||
The sub-parser's **seqan3::argument_parser::info::app_name will be set to the user chosen sub command**. | ||
For example, if the user calls | ||
|
||
``` | ||
max$ ./mygit push -h | ||
``` | ||
|
||
then the sub-parser will be named `mygit-push` and will be instantiated with all arguments | ||
followed by the keyword `push` which in this case triggers printing the help page (`-h`). | ||
|
||
That's it. Here is a full example of a subcommand argument parser you can try and adjust to your needs: | ||
|
||
\include doc/howto/subcommand_argument_parser/subcommand_arg_parse.cpp |
110 changes: 110 additions & 0 deletions
110
doc/howto/subcommand_argument_parser/subcommand_arg_parse.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
#include <seqan3/argument_parser/all.hpp> | ||
|
||
using namespace seqan3; | ||
|
||
// ===================================================================================================================== | ||
// pull | ||
// ===================================================================================================================== | ||
|
||
struct pull_arguments | ||
{ | ||
std::string repository{}; | ||
std::string branch{}; | ||
bool progress{false}; | ||
}; | ||
|
||
int run_git_pull(argument_parser & parser) | ||
{ | ||
pull_arguments args{}; | ||
|
||
parser.add_positional_option(args.repository, "The repository name to pull from."); | ||
parser.add_positional_option(args.branch, "The branch name to pull from."); | ||
|
||
try | ||
{ | ||
parser.parse(); | ||
} | ||
catch (parser_invalid_argument const & ext) | ||
{ | ||
debug_stream << "[Error git pull] " << ext.what() << "\n"; | ||
return -1; | ||
} | ||
|
||
debug_stream << "Git pull with repository " << args.repository << " and branch " << args.branch << std::endl; | ||
|
||
return 0; | ||
} | ||
|
||
// ===================================================================================================================== | ||
// push | ||
// ===================================================================================================================== | ||
|
||
struct push_arguments | ||
{ | ||
std::string repository{}; | ||
std::vector<std::string> branches{}; | ||
bool push_all{false}; | ||
}; | ||
|
||
int run_git_push(argument_parser & parser) | ||
{ | ||
push_arguments args{}; | ||
|
||
parser.add_positional_option(args.repository, "The repository name to push to."); | ||
parser.add_positional_option(args.branches, "The branch names to push (if none are given, push current)."); | ||
|
||
try | ||
{ | ||
parser.parse(); | ||
} | ||
catch (parser_invalid_argument const & ext) | ||
{ | ||
debug_stream << "[Error git push] " << ext.what() << "\n"; | ||
return -1; | ||
} | ||
|
||
debug_stream << "Git push with repository " << args.repository << " and branches " << args.branches << std::endl; | ||
|
||
return 0; | ||
} | ||
|
||
// ===================================================================================================================== | ||
// main | ||
// ===================================================================================================================== | ||
|
||
int main(int argc, char const ** argv) | ||
{ | ||
//![construction] | ||
argument_parser top_level_parser{"mygit", argc, argv, true, {"push", "pull"}}; | ||
//![construction] | ||
|
||
// Add information and flags to your top-level parser just as you would do with a normal one. | ||
// Note that all flags directed at the top-level parser must be specified BEFORE the subcommand key word. | ||
// Because of ambiguity, we do not allow any (positional) options for the top-level parser. | ||
top_level_parser.info.description.push_back("You can push or pull from a remote repository."); | ||
bool flag{false}; | ||
top_level_parser.add_flag(flag, 'f', "flag", "some flag"); | ||
|
||
try | ||
{ | ||
top_level_parser.parse(); // trigger command line parsing | ||
} | ||
catch (parser_invalid_argument const & ext) // catch user errors | ||
{ | ||
debug_stream << "[Error] " << ext.what() << "\n"; // customise your error message | ||
return -1; | ||
} | ||
|
||
//![get_sub_parser] | ||
argument_parser & sub_parser = top_level_parser.get_sub_parser(); // hold a reference to the sub_parser | ||
//![get_sub_parser] | ||
|
||
std::cout << "Proceed to sub parser." << std::endl; | ||
|
||
if (sub_parser.info.app_name == std::string_view{"mygit-pull"}) | ||
run_git_pull(sub_parser); | ||
else if (sub_parser.info.app_name == std::string_view{"mygit-push"}) | ||
run_git_push(sub_parser); | ||
else | ||
throw std::logic_error{"I do not know sub parser " + sub_parser.info.app_name}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.