Skip to content

Commit 0aed6de

Browse files
committed
[MISC] Allow recursive subcommands
1 parent d48c51f commit 0aed6de

File tree

1 file changed

+90
-14
lines changed

1 file changed

+90
-14
lines changed

include/sharg/parser.hpp

Lines changed: 90 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -186,21 +186,13 @@ class parser
186186
char const * const * const argv,
187187
update_notifications version_updates = update_notifications::on,
188188
std::vector<std::string> subcommands = {}) :
189-
version_check_dev_decision{version_updates},
190-
subcommands{std::move(subcommands)}
189+
argc{argc},
190+
argv{argv},
191+
version_check_dev_decision{version_updates}
191192
{
192-
for (auto & sub : this->subcommands)
193-
{
194-
if (!std::regex_match(sub, app_name_regex))
195-
{
196-
throw design_error{"The subcommand name must only contain alpha-numeric characters or '_' and '-' "
197-
"(regex: \"^[a-zA-Z0-9_-]+$\")."};
198-
}
199-
}
200-
201193
info.app_name = app_name;
202194

203-
init(argc, argv);
195+
add_subcommands(std::move(subcommands));
204196
}
205197

206198
//!\brief The destructor.
@@ -666,13 +658,88 @@ class parser
666658
*/
667659
parser_meta_data info;
668660

661+
void add_subcommands(std::vector<std::string> const & subcommands)
662+
{
663+
for (auto const & sub : subcommands)
664+
{
665+
if (!std::regex_match(sub, app_name_regex))
666+
{
667+
std::string const error_message =
668+
detail::to_string(std::quoted(info.app_name),
669+
" contains an invalid subcommand name: ",
670+
std::quoted(sub),
671+
". The subcommand name must only contain alpha-numeric characters ",
672+
"or '_' and '-' (regex: \"^[a-zA-Z0-9_-]+$\").");
673+
throw design_error{error_message};
674+
};
675+
}
676+
677+
auto & parser_subcommands = this->subcommands;
678+
679+
#if 0
680+
auto sorted = subcommands;
681+
std::ranges::sort(sorted);
682+
std::vector<std::string> difference{};
683+
std::ranges::set_intersection(sorted, std::ranges::unique(sorted), std::back_inserter(difference));
684+
if (!difference.empty())
685+
{
686+
auto view = std::views::transform(difference,
687+
[](auto const & val)
688+
{
689+
return std::quoted(val);
690+
});
691+
std::string const error_message =
692+
detail::to_string("add_subcommands()'s arguments list contains duplicate elements for ",
693+
std::quoted(info.app_name),
694+
" : ",
695+
view,
696+
".");
697+
throw design_error{error_message};
698+
}
699+
700+
auto duplicate_subcommands = std::ranges::search(parser_subcommands, subcommands);
701+
if (duplicate_subcommands)
702+
{
703+
auto view = std::views::transform(duplicate_subcommands,
704+
[](auto const & val)
705+
{
706+
return std::quoted(val);
707+
});
708+
std::string const error_message =
709+
detail::to_string(std::quoted(info.app_name), " already contains subcommands: ", view, ".");
710+
throw design_error{error_message};
711+
}
712+
#endif
713+
714+
#ifdef __cpp_lib_containers_ranges
715+
parser_subcommands.append_range(subcommands);
716+
#else
717+
parser_subcommands.insert(parser_subcommands.end(), subcommands.cbegin(), subcommands.cend());
718+
#endif
719+
720+
std::ranges::sort(parser_subcommands);
721+
auto const [first, last] = std::ranges::unique(parser_subcommands);
722+
parser_subcommands.erase(first, last);
723+
724+
init();
725+
}
726+
669727
private:
670728
//!\brief Keeps track of whether the parse function has been called already.
671729
bool parse_was_called{false};
672730

731+
//!\brief Keeps track of whether the init function has been called already.
732+
bool init_was_called{false};
733+
673734
//!\brief Keeps track of whether the user has added a positional list option to check if this was the very last.
674735
bool has_positional_list_option{false};
675736

737+
//!\brief The number of command line arguments.
738+
int argc{};
739+
740+
//!\brief The command line arguments.
741+
char const * const * argv{nullptr};
742+
676743
//!\brief Set on construction and indicates whether the developer deactivates the version check calls completely.
677744
update_notifications version_check_dev_decision{};
678745

@@ -755,10 +822,19 @@ class parser
755822
*
756823
* If `--export-help` is specified with a value other than html, man, cwl or ctd, an sharg::parser_error is thrown.
757824
*/
758-
void init(int argc, char const * const * const argv)
825+
void init()
759826
{
760827
assert(argc > 0);
761-
executable_name.emplace_back(argv[0]);
828+
829+
if (init_was_called)
830+
{
831+
cmd_arguments.clear();
832+
}
833+
else
834+
{
835+
executable_name.emplace_back(argv[0]);
836+
init_was_called = true;
837+
}
762838

763839
bool special_format_was_set{false};
764840

0 commit comments

Comments
 (0)