SeqAn3  3.0.1
The Modern C++ library for sequence analysis.
How to write an argument parser with subcommands

This HowTo shows you how to write an argument parser with subcommand like git push using SeqAn.

DifficultyEasy
Duration15 min
Prerequisite tutorialsParsing command line arguments with SeqAn
Recommended reading

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 argument parser tutorial 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:

seqan3::argument_parser top_level_parser{"mygit", argc, argv, true, {"push", "pull"}};
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():

seqan3::argument_parser & sub_parser = top_level_parser.get_sub_parser(); // hold a reference to the 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:

// =====================================================================================================================
// pull
// =====================================================================================================================
struct pull_arguments
{
std::string repository{};
std::string branch{};
bool progress{false};
};
int run_git_pull(seqan3::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 (seqan3::parser_invalid_argument const & ext)
{
seqan3::debug_stream << "[Error git pull] " << ext.what() << "\n";
return -1;
}
seqan3::debug_stream << "Git pull with repository " << args.repository << " and branch " << args.branch << '\n';
return 0;
}
// =====================================================================================================================
// push
// =====================================================================================================================
struct push_arguments
{
std::string repository{};
bool push_all{false};
};
int run_git_push(seqan3::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 (seqan3::parser_invalid_argument const & ext)
{
seqan3::debug_stream << "[Error git push] " << ext.what() << "\n";
return -1;
}
seqan3::debug_stream << "Git push with repository " << args.repository << " and branches " << args.branches << '\n';
return 0;
}
// =====================================================================================================================
// main
// =====================================================================================================================
int main(int argc, char const ** argv)
{
seqan3::argument_parser top_level_parser{"mygit", argc, argv, true, {"push", "pull"}};
// 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 (seqan3::parser_invalid_argument const & ext) // catch user errors
{
seqan3::debug_stream << "[Error] " << ext.what() << "\n"; // customise your error message
return -1;
}
seqan3::argument_parser & sub_parser = top_level_parser.get_sub_parser(); // hold a reference to the sub_parser
std::cout << "Proceed to sub parser.\n";
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};
}
std::string
seqan3::argument_parser::get_sub_parser
argument_parser & get_sub_parser()
Returns a reference to the sub-parser instance if subcommand parsing was enabled.
Definition: argument_parser.hpp:410
seqan3::argument_parser_meta_data::description
std::vector< std::string > description
A more detailed description that is displayed on the help page in the section "DESCRIPTION"....
Definition: auxiliary.hpp:302
std::string_view
seqan3::argument_parser
The SeqAn command line parser.
Definition: argument_parser.hpp:153
std::vector< std::string >
std::vector::push_back
T push_back(T... args)
std::cout
std::logic_error
all.hpp
Meta-Header for the argument parser module.
seqan3::parser_invalid_argument
Argument parser exception that is thrown whenever there is an error while parsing the command line ar...
Definition: exceptions.hpp:37
seqan3::argument_parser::add_positional_option
void add_positional_option(option_type &value, std::string const &desc, validator_type validator=validator_type{})
Adds a positional option to the seqan3::argument_parser.
Definition: argument_parser.hpp:299
seqan3::argument_parser::info
argument_parser_meta_data info
Aggregates all parser related meta data (see seqan3::argument_parser_meta_data struct).
Definition: argument_parser.hpp:525
seqan3::debug_stream
debug_stream_type debug_stream
A global instance of seqan3::debug_stream_type.
Definition: debug_stream.hpp:39
seqan3::argument_parser::parse
void parse()
Initiates the actual command line parsing.
Definition: argument_parser.hpp:389
seqan3::field::flag
The alignment flag (bit information), uint16_t value.
seqan3::argument_parser_meta_data::app_name
std::string app_name
The application name that will be displayed on the help page.
Definition: auxiliary.hpp:268
std::invalid_argument::what
T what(T... args)