@@ -423,7 +423,8 @@ class parser
423
423
// User input sanitization must happen before version check!
424
424
verify_app_and_subcommand_names ();
425
425
426
- init ();
426
+ // Determine the format and subcommand.
427
+ determine_format_and_subcommand ();
427
428
428
429
// If a subcommand was provided, check that it is valid.
429
430
verify_subcommand ();
@@ -741,7 +742,7 @@ class parser
741
742
detail::format_man,
742
743
detail::format_tdl,
743
744
detail::format_copyright>
744
- format{detail::format_help{{}, {}, false }}; // Will be overwritten in any case.
745
+ format{detail::format_short_help{}};
745
746
746
747
// !\brief List of option/flag identifiers that are already used.
747
748
std::set<std::string> used_option_ids{" h" , " hh" , " help" , " advanced-help" , " export-help" , " version" , " copyright" };
@@ -784,98 +785,104 @@ class parser
784
785
*
785
786
* If `--export-help` is specified with a value other than html, man, cwl or ctd, an sharg::parser_error is thrown.
786
787
*/
787
- void init ()
788
+ void determine_format_and_subcommand ()
788
789
{
789
790
assert (!arguments.empty ());
791
+ auto it = arguments.begin ();
790
792
791
- executable_name.emplace_back (arguments[0 ]);
792
-
793
- bool special_format_was_set{false };
793
+ executable_name.emplace_back (*it);
794
+ ++it;
794
795
795
796
// Helper function for going to the next argument. This makes it more obvious that we are
796
797
// 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
798
799
{
800
+ assert (it != arguments.end ());
801
+
799
802
if (++it == arguments.end ())
800
803
throw too_few_arguments{message.data ()};
801
804
};
802
805
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
805
808
{
806
- std::string_view arg{*it};
809
+ if (subcommands.empty ())
810
+ return false ;
807
811
808
- if (! subcommands.empty ()) // this is a top_level parser
812
+ if (std::ranges::find (subcommands, arg) != subcommands.end ())
809
813
{
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 (' -' ))
824
831
{
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 ;
833
834
}
834
835
}
835
836
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
+
836
848
if (arg == " -h" || arg == " --help" )
837
849
{
838
- special_format_was_set = true ;
839
850
format = detail::format_help{subcommands, version_check_dev_decision, false };
840
851
}
841
852
else if (arg == " -hh" || arg == " --advanced-help" )
842
853
{
843
- special_format_was_set = true ;
844
854
format = detail::format_help{subcommands, version_check_dev_decision, true };
845
855
}
846
856
else if (arg == " --version" )
847
857
{
848
- special_format_was_set = true ;
849
858
format = detail::format_version{};
850
859
}
851
860
else if (arg == " --copyright" )
852
861
{
853
- special_format_was_set = true ;
854
862
format = detail::format_copyright{};
855
863
}
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=" ))
857
865
{
858
- special_format_was_set = true ;
866
+ arg. remove_prefix (std::string_view{ " --export-help " }. size ()) ;
859
867
860
- std::string_view export_format;
861
-
862
- if (arg.size () > 13 )
868
+ // --export-help man
869
+ if (arg.empty ())
863
870
{
864
- export_format = arg.substr (14 );
871
+ go_to_next_arg (" Option --export-help must be followed by a value." );
872
+ arg = *it;
865
873
}
866
- else
874
+ else // --export-help=man
867
875
{
868
- go_to_next_arg (it, " Option --export-help must be followed by a value." );
869
- export_format = *it;
876
+ arg.remove_prefix (1u );
870
877
}
871
878
872
- if (export_format == " html" )
879
+ if (arg == " html" )
873
880
format = detail::format_html{subcommands, version_check_dev_decision};
874
- else if (export_format == " man" )
881
+ else if (arg == " man" )
875
882
format = detail::format_man{subcommands, version_check_dev_decision};
876
- else if (export_format == " ctd" )
883
+ else if (arg == " ctd" )
877
884
format = detail::format_tdl{detail::format_tdl::FileFormat::CTD};
878
- else if (export_format == " cwl" )
885
+ else if (arg == " cwl" )
879
886
format = detail::format_tdl{detail::format_tdl::FileFormat::CWL};
880
887
else
881
888
throw validation_error{" Validation failed for option --export-help: "
@@ -884,7 +891,7 @@ class parser
884
891
}
885
892
else if (arg == " --version-check" )
886
893
{
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." );
888
895
arg = *it;
889
896
890
897
if (arg == " 1" || arg == " true" )
@@ -900,14 +907,13 @@ class parser
900
907
}
901
908
}
902
909
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))
904
912
return ;
905
913
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)
911
917
format = detail::format_parse (format_arguments);
912
918
}
913
919
0 commit comments