diff --git a/doc/howto/subcommand_parser/index.md b/doc/howto/subcommand_parser/index.md index 174c5072..5c483404 100644 --- a/doc/howto/subcommand_parser/index.md +++ b/doc/howto/subcommand_parser/index.md @@ -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 diff --git a/include/sharg/auxiliary.hpp b/include/sharg/auxiliary.hpp index 574cd163..ac98b494 100644 --- a/include/sharg/auxiliary.hpp +++ b/include/sharg/auxiliary.hpp @@ -77,6 +77,20 @@ class vector_of_string : public std::vector * 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.} @@ -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}; @@ -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 description; + std::vector description{}; /*!\brief Add lines of usage to the synopsis section of the help page (e.g. * "./my_read_mapper [OPTIONS] FILE1 FILE1"). */ - std::vector synopsis; + std::vector 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 examples; + std::vector examples{}; + + //!\cond + constexpr friend bool operator==(parser_meta_data, parser_meta_data) = default; + //!\endcond }; } // namespace sharg diff --git a/include/sharg/parser.hpp b/include/sharg/parser.hpp index becab897..8b504e32 100644 --- a/include/sharg/parser.hpp +++ b/include/sharg/parser.hpp @@ -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 @@ -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. @@ -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(info.app_name + "-" + arg.data(), std::vector{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 diff --git a/test/unit/parser/subcommand_test.cpp b/test/unit/parser/subcommand_test.cpp index 85ebfa0c..e3993846 100644 --- a/test/unit/parser/subcommand_test.cpp +++ b/test/unit/parser/subcommand_test.cpp @@ -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); +}