Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions doc/howto/subcommand_parser/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ 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`).

Additionally, the following metadata will be copied from the top-level parser to the sub-parser:
* sharg::parser::info::version
* sharg::parser::info::author
* sharg::parser::info::email
* sharg::parser::info::date
* sharg::parser::info::url
* sharg::parser::info::short_copyright
* sharg::parser::info::long_copyright
* sharg::parser::info::citation

That's it. Here is a full example of a subcommand parser you can try and adjust to your needs:

\include doc/howto/subcommand_parser/subcommand_parse.cpp
Expand Down
46 changes: 32 additions & 14 deletions include/sharg/auxiliary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,20 @@ class vector_of_string : public std::vector<std::string>
* The meta information is assembled in a struct to provide a central access
* point that can be easily extended.
*
* ### Subparser
*
* When \link subcommand_parse using a subparser \endlink, the subcommand will be appended to the
* subparser's #app_name.
* Additionally, the following metadata will be copied from the top-level parser to the sub-parser:
* * #version
* * #author
* * #email
* * #date
* * #url
* * #short_copyright
* * #long_copyright
* * #citation
*
* \remark For a complete overview, take a look at \ref parser
*
* \stableapi{Since version 1.0.}
Expand All @@ -88,43 +102,43 @@ struct parser_meta_data // holds all meta information
* The application name must only contain alpha-numeric characters, '_' or '-',
* i.e. the following regex must evaluate to true: `\"^[a-zA-Z0-9_-]+$\"`.
*/
std::string app_name;
std::string app_name{};

//!\brief The version information `MAJOR.MINOR.PATH` (e.g. 3.1.3)
std::string version;
std::string version{};

//!\brief A short description of the application (e.g. "A tool for mapping reads to the genome").
std::string short_description;
std::string short_description{};

//!\brief Your name ;-)
std::string author;
std::string author{};

//!\brief The author's e-mail address for correspondence.
std::string email;
std::string email{};

/*!\brief The date that the application was last updated. Keep this updated,
*! since it will tell your users that the application is maintained.
*/
std::string date;
std::string date{};

//!\brief A link to your github/gitlab project with the newest release.
std::string url;
std::string url{};

//!\brief Brief copyright (and/or license) information.
std::string short_copyright;
std::string short_copyright{};

/*!\brief Detailed copyright information that will be displayed
* when the user specifies "--copyright" on the command line.
*/
std::string long_copyright;
std::string long_copyright{};

//!\brief How users shall cite your application.
vector_of_string citation;
vector_of_string citation{};

/*!\brief The title of your man page when exported by specifying
* "--export-help man" on the common line.
*/
std::string man_page_title;
std::string man_page_title{};

//!\brief The man page section info (type `man man` on the command line for more information).
unsigned man_page_section{1};
Expand All @@ -134,18 +148,22 @@ struct parser_meta_data // holds all meta information
* to the description vector will be treated as a paragraph and
* is separated by a new line.
*/
std::vector<std::string> description;
std::vector<std::string> description{};

/*!\brief Add lines of usage to the synopsis section of the help page (e.g.
* "./my_read_mapper [OPTIONS] FILE1 FILE1").
*/
std::vector<std::string> synopsis;
std::vector<std::string> synopsis{};

/*!\brief Provide some examples on how to use your tool and what standard
* parameters might be appropriate in different cases (e.g.
* "./my_read_mapper -s 3 --my_flag path/infile1").
*/
std::vector<std::string> examples;
std::vector<std::string> examples{};

//!\cond
constexpr friend bool operator==(parser_meta_data, parser_meta_data) = default;
//!\endcond
};

} // namespace sharg
17 changes: 17 additions & 0 deletions include/sharg/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ namespace sharg
* which means that applications ship with less bugs.
* For privacy implications, please see: https://docs.seqan.de/sharg/main_user/about_update_notifications.html.
*
* In brief, an enabled version check will not transmit any data unless it is *explicitly* granted permission to do so.
* `sharg::update_notifications` therefore controls whether an application may ask for that permission.
*
* Developers that wish to disable this feature permanently can pass an extra constructor argument:
*
* \include doc/tutorial/parser/disable_version_check.cpp
Expand All @@ -144,6 +147,7 @@ namespace sharg
*
* * disabling it for a specific application simply by setting the option `--version-check false/0` or
* * disabling it for all applications by setting the `SHARG_NO_VERSION_CHECK` environment variable.
* * selecting `n` (never) when asked by the application.
*
* Note that in case there is no `--version-check` option (display available options with `-h/--help)`,
* then the developer already disabled the version check functionality.
Expand Down Expand Up @@ -834,11 +838,24 @@ class parser
if (subcommands.empty())
return false;

auto copy_metadata_to_subparser = [this](parser & sub_parser)
{
sub_parser.info.version = info.version;
sub_parser.info.author = info.author;
sub_parser.info.email = info.email;
sub_parser.info.date = info.date;
sub_parser.info.url = info.url;
sub_parser.info.short_copyright = info.short_copyright;
sub_parser.info.long_copyright = info.long_copyright;
sub_parser.info.citation = info.citation;
};

if (std::ranges::find(subcommands, arg) != subcommands.end())
{
sub_parser = std::make_unique<parser>(info.app_name + "-" + arg.data(),
std::vector<std::string>{it, arguments.end()},
update_notifications::off);
copy_metadata_to_subparser(get_sub_parser());

// Add the original calls to the front, e.g. ["raptor"],
// s.t. ["raptor", "build"] will be the list after constructing the subparser
Expand Down
53 changes: 53 additions & 0 deletions test/unit/parser/subcommand_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,56 @@ TEST_F(subcommand_test, recursive_subcommands)

EXPECT_EQ(get_parse_cout_on_exit(sub_sub_parser), expected_sub_sub_full_help);
}

TEST_F(subcommand_test, copy_meta_data)
{
sharg::parser_meta_data info{.version = "1.0.0",
.author = "SeqAn-Team",
.email = "mail@example.org",
.date = "1970-01-01",
.url = "example.org",
.short_copyright = "BSD 3-Clause",
.long_copyright = "BSD 3-Clause Text",
.citation = "Cite me!"};

auto parser = get_subcommand_parser({"index", "--help"}, {"index"});
info.app_name = parser.info.app_name;
parser.info = info;
EXPECT_EQ(parser.info.app_name, "test_parser");
ASSERT_EQ(parser.info, info); // Sanity check for test setup
EXPECT_NO_THROW(parser.parse());

auto & sub_parser = parser.get_sub_parser();
EXPECT_EQ(sub_parser.info.app_name, "test_parser-index");
info.app_name = sub_parser.info.app_name;
EXPECT_EQ(sub_parser.info, info);

std::string expected_sub_full_help = "test_parser-index\n"
"=================\n"
"\n"
"OPTIONS\n"
"\n"
+ basic_options_str
+ "\n"
"VERSION\n"
" Last update: 1970-01-01\n"
" test_parser-index version: 1.0.0\n"
" Sharg version: "
+ sharg::sharg_version_cstring
+ "\n"
"\n"
"URL\n"
" example.org\n"
"\n"
"LEGAL\n"
" test_parser-index Copyright: BSD 3-Clause\n"
" Author: SeqAn-Team\n"
" Contact: mail@example.org\n"
" SeqAn Copyright: 2006-2025 Knut Reinert, FU-Berlin; released under the\n"
" 3-clause BSDL.\n"
" In your academic works please cite:\n"
" [1] Cite me!\n"
" For full copyright and/or warranty information see --copyright.\n";

EXPECT_EQ(get_parse_cout_on_exit(sub_parser), expected_sub_full_help);
}
Loading