Skip to content

Commit d44ad98

Browse files
committed
[MISC] Refactor init()
1 parent 07d5fc4 commit d44ad98

File tree

2 files changed

+65
-59
lines changed

2 files changed

+65
-59
lines changed

include/sharg/parser.hpp

Lines changed: 63 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,8 @@ class parser
423423
// User input sanitization must happen before version check!
424424
verify_app_and_subcommand_names();
425425

426-
init();
426+
// Determine the format and subcommand.
427+
determine_format_and_subcommand();
427428

428429
// If a subcommand was provided, check that it is valid.
429430
verify_subcommand();
@@ -741,7 +742,7 @@ class parser
741742
detail::format_man,
742743
detail::format_tdl,
743744
detail::format_copyright>
744-
format{detail::format_help{{}, {}, false}}; // Will be overwritten in any case.
745+
format{detail::format_short_help{}};
745746

746747
//!\brief List of option/flag identifiers that are already used.
747748
std::set<std::string> used_option_ids{"h", "hh", "help", "advanced-help", "export-help", "version", "copyright"};
@@ -784,98 +785,104 @@ class parser
784785
*
785786
* If `--export-help` is specified with a value other than html, man, cwl or ctd, an sharg::parser_error is thrown.
786787
*/
787-
void init()
788+
void determine_format_and_subcommand()
788789
{
789790
assert(!arguments.empty());
791+
auto it = arguments.begin();
790792

791-
executable_name.emplace_back(arguments[0]);
792-
793-
bool special_format_was_set{false};
793+
executable_name.emplace_back(*it);
794+
++it;
794795

795796
// Helper function for going to the next argument. This makes it more obvious that we are
796797
// incrementing `it` (version-check, and export-help).
797-
auto go_to_next_arg = [this](auto & it, std::string_view message) -> auto
798+
auto go_to_next_arg = [this, &it](std::string_view message) -> auto
798799
{
800+
assert(it != arguments.end());
801+
799802
if (++it == arguments.end())
800803
throw too_few_arguments{message.data()};
801804
};
802805

803-
// start at 1 to skip binary name
804-
for (auto it = ++arguments.begin(); it != arguments.end(); ++it)
806+
// Helper function for finding and processing subcommands.
807+
auto found_and_processed_subcommand = [this, &it](std::string_view arg) -> bool
805808
{
806-
std::string_view arg{*it};
809+
if (subcommands.empty())
810+
return false;
807811

808-
if (!subcommands.empty()) // this is a top_level parser
812+
if (std::ranges::find(subcommands, arg) != subcommands.end())
809813
{
810-
if (std::ranges::find(subcommands, arg) != subcommands.end()) // identified subparser
811-
{
812-
sub_parser = std::make_unique<parser>(info.app_name + "-" + arg.data(),
813-
std::vector<std::string>{it, arguments.end()},
814-
update_notifications::off);
815-
816-
// Add the original calls to the front, e.g. ["raptor"],
817-
// s.t. ["raptor", "build"] will be the list after constructing the subparser
818-
sub_parser->executable_name.insert(sub_parser->executable_name.begin(),
819-
executable_name.begin(),
820-
executable_name.end());
821-
break;
822-
}
823-
else
814+
sub_parser = std::make_unique<parser>(info.app_name + "-" + arg.data(),
815+
std::vector<std::string>{it, arguments.end()},
816+
update_notifications::off);
817+
818+
// Add the original calls to the front, e.g. ["raptor"],
819+
// s.t. ["raptor", "build"] will be the list after constructing the subparser
820+
sub_parser->executable_name.insert(sub_parser->executable_name.begin(),
821+
executable_name.begin(),
822+
executable_name.end());
823+
return true;
824+
}
825+
else
826+
{
827+
// Positional options are forbidden by design. Todo: Allow options. Forbidden in check_option_config.
828+
// Flags and options, which both start with '-', are allowed for the top-level parser.
829+
// Otherwise, this is a wrongly spelled subcommand. The error will be thrown in parse().
830+
if (!arg.starts_with('-'))
824831
{
825-
// Options and positional options are forbidden by design.
826-
// Flags starting with '-' are allowed for the top-level parser.
827-
// Otherwise, this is a wrongly spelled subcommand. The error will be thrown in parse().
828-
if (!arg.empty() && arg[0] != '-')
829-
{
830-
format_arguments.emplace_back(arg);
831-
break;
832-
}
832+
format_arguments.emplace_back(arg);
833+
return true;
833834
}
834835
}
835836

837+
return false;
838+
};
839+
840+
// Process the arguments.
841+
for (; it != arguments.end(); ++it)
842+
{
843+
std::string_view arg{*it};
844+
845+
if (found_and_processed_subcommand(arg))
846+
break;
847+
836848
if (arg == "-h" || arg == "--help")
837849
{
838-
special_format_was_set = true;
839850
format = detail::format_help{subcommands, version_check_dev_decision, false};
840851
}
841852
else if (arg == "-hh" || arg == "--advanced-help")
842853
{
843-
special_format_was_set = true;
844854
format = detail::format_help{subcommands, version_check_dev_decision, true};
845855
}
846856
else if (arg == "--version")
847857
{
848-
special_format_was_set = true;
849858
format = detail::format_version{};
850859
}
851860
else if (arg == "--copyright")
852861
{
853-
special_format_was_set = true;
854862
format = detail::format_copyright{};
855863
}
856-
else if (arg.substr(0, 13) == "--export-help") // --export-help=man is also allowed
864+
else if (arg == "--export-help" || arg.starts_with("--export-help="))
857865
{
858-
special_format_was_set = true;
866+
arg.remove_prefix(std::string_view{"--export-help"}.size());
859867

860-
std::string_view export_format;
861-
862-
if (arg.size() > 13)
868+
// --export-help man
869+
if (arg.empty())
863870
{
864-
export_format = arg.substr(14);
871+
go_to_next_arg("Option --export-help must be followed by a value.");
872+
arg = *it;
865873
}
866-
else
874+
else // --export-help=man
867875
{
868-
go_to_next_arg(it, "Option --export-help must be followed by a value.");
869-
export_format = *it;
876+
arg.remove_prefix(1u);
870877
}
871878

872-
if (export_format == "html")
879+
if (arg == "html")
873880
format = detail::format_html{subcommands, version_check_dev_decision};
874-
else if (export_format == "man")
881+
else if (arg == "man")
875882
format = detail::format_man{subcommands, version_check_dev_decision};
876-
else if (export_format == "ctd")
883+
else if (arg == "ctd")
877884
format = detail::format_tdl{detail::format_tdl::FileFormat::CTD};
878-
else if (export_format == "cwl")
885+
else if (arg == "cwl")
879886
format = detail::format_tdl{detail::format_tdl::FileFormat::CWL};
880887
else
881888
throw validation_error{"Validation failed for option --export-help: "
@@ -884,7 +891,7 @@ class parser
884891
}
885892
else if (arg == "--version-check")
886893
{
887-
go_to_next_arg(it, "Option --version-check must be followed by a value.");
894+
go_to_next_arg("Option --version-check must be followed by a value.");
888895
arg = *it;
889896

890897
if (arg == "1" || arg == "true")
@@ -900,14 +907,13 @@ class parser
900907
}
901908
}
902909

903-
if (special_format_was_set)
910+
// A special format was set. We do not need to parse the format_arguments.
911+
if (!std::holds_alternative<detail::format_short_help>(format))
904912
return;
905913

906-
// All special options have been handled. If there are no arguments left and we do not have a subparser,
907-
// we call the short help.
908-
if (format_arguments.empty() && !sub_parser)
909-
format = detail::format_short_help{};
910-
else
914+
// All special options have been handled. If there are arguments left or we have a subparser,
915+
// we call format_parse. Oterhwise, we print the short help (default variant).
916+
if (!format_arguments.empty() || sub_parser)
911917
format = detail::format_parse(format_arguments);
912918
}
913919

test/unit/detail/format_html_test.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ TEST_F(format_html_test, parse_error)
215215
parser = get_parser("./help_add_test", "--export-help", "atml");
216216
EXPECT_THROW(parser.parse(), sharg::validation_error);
217217

218-
// Currently not checking for `=`
218+
// wrong separator
219219
parser = get_parser("./help_add_test", "--export-help#html");
220-
EXPECT_NE(get_parse_cout_on_exit(parser), "");
220+
EXPECT_THROW(parser.parse(), sharg::unknown_option);
221221
}

0 commit comments

Comments
 (0)