26namespace argon_testing {
27struct argument_parser_test_fixture;
179class argument_parser {
181 argument_parser(
const argument_parser&) =
delete;
182 argument_parser& operator=(
const argument_parser&) =
delete;
184 argument_parser(argument_parser&&) =
delete;
185 argument_parser& operator=(argument_parser&&) =
delete;
187 argument_parser(
const std::string_view name) : argument_parser(name,
"") {}
189 ~argument_parser() =
default;
196 argument_parser& program_version(
const version& version)
noexcept {
197 this->_program_version.emplace(version.str());
206 argument_parser& program_version(std::string_view version) {
207 if (util::contains_whitespaces(version))
208 throw invalid_configuration(
"The program version cannot contain whitespace characters!"
211 this->_program_version.emplace(version);
220 argument_parser& program_description(std::string_view description)
noexcept {
221 this->_program_description.emplace(description);
231 argument_parser& verbose(
const bool v =
true) noexcept {
242 argument_parser& unknown_arguments_policy(
const unknown_policy policy)
noexcept {
243 this->_unknown_policy = policy;
254 template <util::c_range_of<default_argument> AR>
255 argument_parser& default_arguments(
const AR& arg_discriminators)
noexcept {
256 for (
const auto arg_discriminator : arg_discriminators)
266 argument_parser& default_arguments(
267 const std::initializer_list<default_argument>& arg_discriminators
269 return this->default_arguments<>(arg_discriminators);
277 argument_parser& default_arguments(
278 const std::same_as<default_argument>
auto... arg_discriminators
280 (detail::add_default_argument(arg_discriminators, *
this), ...);
291 template <util::c_argument_value_type T = std::
string>
292 positional_argument<T>& add_positional_argument(
const std::string_view name) {
293 return this->add_positional_argument<T>(this->_gr_positional_args, name);
303 template <util::c_argument_value_type T = std::
string>
304 positional_argument<T>& add_positional_argument(
305 argument_group& group,
const std::string_view name
307 this->_validate_group(group);
308 this->_verify_arg_name_pattern(name);
310 const detail::argument_name arg_name(std::make_optional<std::string>(name));
311 if (this->_is_arg_name_used(arg_name))
312 throw invalid_configuration::argument_name_used(arg_name);
315 this->_positional_args.emplace_back(std::make_shared<positional_argument<T>>(arg_name));
316 group._add_argument(new_arg_ptr);
317 return static_cast<positional_argument<T>&
>(*new_arg_ptr);
328 template <util::c_argument_value_type T = std::
string>
329 optional_argument<T>& add_optional_argument(
330 const std::string_view name,
331 const detail::argument_name_discriminator name_discr = n_primary
333 return this->add_optional_argument<T>(this->_gr_optional_args, name, name_discr);
344 template <util::c_argument_value_type T = std::
string>
345 optional_argument<T>& add_optional_argument(
346 const std::string_view primary_name,
const std::string_view secondary_name
348 return this->add_optional_argument<T>(
349 this->_gr_optional_args, primary_name, secondary_name
362 template <util::c_argument_value_type T = std::
string>
363 optional_argument<T>& add_optional_argument(
364 argument_group& group,
365 const std::string_view name,
366 const detail::argument_name_discriminator name_discr = n_primary
368 this->_validate_group(group);
369 this->_verify_arg_name_pattern(name);
371 const auto arg_name =
374 argument_name{std::make_optional<std::string>(name), std::nullopt, this->_flag_prefix_char}
375 : detail::argument_name{
376 std::nullopt, std::make_optional<std::string>(name), this->_flag_prefix_char
379 if (this->_is_arg_name_used(arg_name))
380 throw invalid_configuration::argument_name_used(arg_name);
383 this->_optional_args.emplace_back(std::make_shared<optional_argument<T>>(arg_name));
384 group._add_argument(new_arg_ptr);
385 return static_cast<optional_argument<T>&
>(*new_arg_ptr);
397 template <util::c_argument_value_type T = std::
string>
398 optional_argument<T>& add_optional_argument(
399 argument_group& group,
400 const std::string_view primary_name,
401 const std::string_view secondary_name
403 this->_validate_group(group);
404 this->_verify_arg_name_pattern(primary_name);
405 this->_verify_arg_name_pattern(secondary_name);
407 const detail::argument_name arg_name(
408 std::make_optional<std::string>(primary_name),
409 std::make_optional<std::string>(secondary_name),
410 this->_flag_prefix_char
412 if (this->_is_arg_name_used(arg_name))
413 throw invalid_configuration::argument_name_used(arg_name);
416 this->_optional_args.emplace_back(std::make_shared<optional_argument<T>>(arg_name));
417 group._add_argument(new_arg_ptr);
418 return static_cast<optional_argument<T>&
>(*new_arg_ptr);
429 template <
bool StoreImplicitly = true>
430 optional_argument<bool>& add_flag(
431 const std::string_view name,
432 const detail::argument_name_discriminator name_discr = n_primary
434 return this->add_optional_argument<bool>(name, name_discr)
435 .default_values(not StoreImplicitly)
436 .implicit_values(StoreImplicitly)
448 template <
bool StoreImplicitly = true>
449 optional_argument<bool>& add_flag(
450 const std::string_view primary_name,
const std::string_view secondary_name
452 return this->add_optional_argument<bool>(primary_name, secondary_name)
453 .default_values(not StoreImplicitly)
454 .implicit_values(StoreImplicitly)
467 template <
bool StoreImplicitly = true>
468 optional_argument<bool>& add_flag(
469 argument_group& group,
470 const std::string_view name,
471 const detail::argument_name_discriminator name_discr = n_primary
473 return this->add_optional_argument<bool>(group, name, name_discr)
474 .default_values(not StoreImplicitly)
475 .implicit_values(StoreImplicitly)
488 template <
bool StoreImplicitly = true>
489 optional_argument<bool>& add_flag(
490 argument_group& group,
491 const std::string_view primary_name,
492 const std::string_view secondary_name
494 return this->add_optional_argument<bool>(group, primary_name, secondary_name)
495 .default_values(not StoreImplicitly)
496 .implicit_values(StoreImplicitly)
505 argument_group& add_group(
const std::string_view name)
noexcept {
506 return *this->_argument_groups.emplace_back(argument_group::create(*
this, name));
514 argument_parser& add_subparser(
const std::string_view name) {
515 const auto subparser_it = std::ranges::find(
516 this->_subparsers, name, [](
const auto& subparser) {
return subparser->_name; }
518 if (subparser_it != this->_subparsers.end())
519 throw std::logic_error(std::format(
520 "A subparser with the given name () already exists in parser '{}'",
521 (*subparser_it)->_name,
525 return *this->_subparsers.emplace_back(
526 std::unique_ptr<argument_parser>(
new argument_parser(name, this->_program_name))
543 void parse_args(
int argc,
char* argv[]) {
544 this->parse_args(std::span(argv + 1,
static_cast<std::size_t
>(argc - 1)));
555 template <util::c_forward_range_of<std::
string, util::type_val
idator::convertible> AR>
556 void parse_args(
const AR& argv_rng) {
557 parsing_state state(*
this);
558 this->_parse_args_impl(std::ranges::begin(argv_rng), std::ranges::end(argv_rng), state);
560 if (not state.unknown_args.empty())
561 throw parsing_failure(std::format(
562 "Failed to deduce the argument for values [{}]", util::join(state.unknown_args)
578 void try_parse_args(
int argc,
char* argv[]) {
579 this->try_parse_args(std::span(argv + 1,
static_cast<std::size_t
>(argc - 1)));
594 template <util::c_forward_range_of<std::
string, util::type_val
idator::convertible> AR>
595 void try_parse_args(
const AR& argv_rng) {
597 this->parse_args(argv_rng);
600 std::cerr <<
"[argon::error] " << err.what() << std::endl
601 << this->resolved_parser() << std::endl;
602 std::exit(EXIT_FAILURE);
624 std::vector<std::string> parse_known_args(
int argc,
char* argv[]) {
625 return this->parse_known_args(std::span(argv + 1,
static_cast<std::size_t
>(argc - 1)));
642 template <util::c_forward_range_of<std::
string, util::type_val
idator::convertible> AR>
643 std::vector<std::string> parse_known_args(
const AR& argv_rng) {
644 parsing_state state(*
this,
true);
645 this->_parse_args_impl(std::ranges::begin(argv_rng), std::ranges::end(argv_rng), state);
646 return std::move(state.unknown_args);
662 std::vector<std::string> try_parse_known_args(
int argc,
char* argv[]) {
663 return this->try_parse_known_args(std::span(argv + 1,
static_cast<std::size_t
>(argc - 1)));
679 template <util::c_forward_range_of<std::
string, util::type_val
idator::convertible> AR>
680 std::vector<std::string> try_parse_known_args(
const AR& argv_rng) {
682 return this->parse_known_args(argv_rng);
685 std::cerr <<
"[argon::error] " << err.what() << std::endl
686 << this->resolved_parser() << std::endl;
687 std::exit(EXIT_FAILURE);
692 [[nodiscard]] std::string_view name() const noexcept {
706 [[nodiscard]] std::string_view program_name() const noexcept {
707 return this->_program_name;
715 [[nodiscard]]
bool invoked() const noexcept {
716 return this->_invoked;
723 [[nodiscard]]
bool finalized() const noexcept {
724 return this->_finalized;
731 [[nodiscard]] argument_parser& resolved_parser() noexcept {
732 const auto used_subparser_it = std::ranges::find_if(
733 this->_subparsers, [](
const auto& subparser) {
return subparser->_invoked; }
735 if (used_subparser_it == this->_subparsers.end())
737 return (*used_subparser_it)->resolved_parser();
745 [[nodiscard]]
bool is_used(std::string_view arg_name)
const noexcept {
746 const auto arg = this->_get_argument(arg_name);
747 return arg ? arg->is_used() :
false;
755 [[nodiscard]]
bool has_value(std::string_view arg_name)
const noexcept {
756 const auto arg = this->_get_argument(arg_name);
757 return arg ? arg->has_value() :
false;
765 [[nodiscard]] std::size_t count(std::string_view arg_name)
const noexcept {
766 const auto arg = this->_get_argument(arg_name);
767 return arg ? arg->count() : 0ull;
777 template <util::c_argument_value_type T = std::
string>
778 [[nodiscard]] T value(std::string_view arg_name)
const {
779 const auto arg = this->_get_argument(arg_name);
781 throw lookup_failure::argument_not_found(arg_name);
783 const auto& arg_value = arg->value();
785 return std::any_cast<T>(arg_value);
787 catch (
const std::bad_any_cast&) {
788 throw type_error::invalid_value_type<T>(arg->name());
801 template <util::c_argument_value_type T = std::
string, std::convertible_to<T> U>
802 [[nodiscard]] T value_or(std::string_view arg_name, U&& fallback_value)
const {
803 const auto arg = this->_get_argument(arg_name);
805 throw lookup_failure::argument_not_found(arg_name);
808 const auto& arg_value = arg->value();
809 return std::any_cast<T>(arg_value);
811 catch (
const std::logic_error&) {
814 return T{std::forward<U>(fallback_value)};
816 catch (
const std::bad_any_cast&) {
817 throw type_error::invalid_value_type<T>(arg->name());
829 template <util::c_argument_value_type T = std::
string>
830 [[nodiscard]] std::vector<T> values(std::string_view arg_name)
const {
831 const auto arg = this->_get_argument(arg_name);
833 throw lookup_failure::argument_not_found(arg_name);
836 std::vector<T> values;
838 util::any_range_cast_view<T>(arg->values()), std::back_inserter(values)
842 catch (
const std::bad_any_cast&) {
843 throw type_error::invalid_value_type<T>(arg->name());
852 void print_help(
const bool verbose, std::ostream& os = std::cout)
const noexcept {
853 os <<
"Program: " << this->_program_name;
854 if (this->_program_version)
855 os <<
" (" << this->_program_version.value() <<
')';
858 if (this->_program_description)
860 << std::string(this->_indent_width,
' ') << this->_program_description.value()
863 this->_print_subparsers(os);
864 for (
const auto& group : this->_argument_groups)
865 this->_print_group(os, *group, verbose);
875 void print_version(std::ostream& os = std::cout)
const noexcept {
876 os << this->_program_name <<
" : version " << this->_program_version.value_or(
"unspecified")
890 friend std::ostream&
operator<<(std::ostream& os,
const argument_parser& parser)
noexcept {
891 parser.print_help(parser._verbose, os);
897 friend struct ::argon_testing::argument_parser_test_fixture;
901 using arg_ptr_t = std::shared_ptr<detail::argument_base>;
902 using arg_ptr_vec_t = std::vector<arg_ptr_t>;
903 using arg_ptr_vec_iter_t =
typename arg_ptr_vec_t::iterator;
905 using arg_group_ptr_t = std::unique_ptr<argument_group>;
906 using arg_group_ptr_vec_t = std::vector<arg_group_ptr_t>;
908 using arg_parser_ptr_t = std::unique_ptr<argument_parser>;
909 using arg_parser_ptr_vec_t = std::vector<arg_parser_ptr_t>;
911 using arg_token_vec_t = std::vector<detail::argument_token>;
912 using arg_token_vec_iter_t =
typename arg_token_vec_t::const_iterator;
915 struct parsing_state {
916 parsing_state(argument_parser& parser,
const bool parse_known_only =
false)
918 curr_pos_arg_it(parser._positional_args.begin()),
919 parse_known_only(parse_known_only) {}
923 void set_parser(argument_parser& parser) {
924 this->curr_arg =
nullptr;
925 this->curr_pos_arg_it = parser._positional_args.begin();
933 std::vector<std::string> unknown_args = {};
936 argument_parser(
const std::string_view name,
const std::string_view parent_name)
939 std::format(
"{}{}{}", parent_name, std::string(not parent_name.empty(),
' '), name)
941 _gr_positional_args(add_group(
"Positional Arguments")),
942 _gr_optional_args(add_group(
"Optional Arguments")) {
944 throw invalid_configuration(
"The program name cannot be empty!");
946 if (util::contains_whitespaces(name))
947 throw invalid_configuration(
"The program name cannot contain whitespace characters!");
954 void _verify_arg_name_pattern(
const std::string_view arg_name)
const {
955 if (arg_name.empty())
956 throw invalid_configuration::invalid_argument_name(
957 arg_name,
"An argument name cannot be empty."
960 if (util::contains_whitespaces(arg_name))
961 throw invalid_configuration::invalid_argument_name(
962 arg_name,
"An argument name cannot contain whitespaces."
965 if (arg_name.front() == this->_flag_prefix_char)
966 throw invalid_configuration::invalid_argument_name(
969 "An argument name cannot begin with a flag prefix character ({}).",
970 this->_flag_prefix_char
974 if (std::isdigit(arg_name.front()))
975 throw invalid_configuration::invalid_argument_name(
976 arg_name,
"An argument name cannot begin with a digit."
986 [[nodiscard]]
auto _name_match_predicate(
987 const std::string_view arg_name,
988 const detail::argument_name::match_type m_type = detail::argument_name::m_any
990 return [=](
const arg_ptr_t& arg) {
return arg->name().match(arg_name, m_type); };
999 [[nodiscard]]
auto _name_match_predicate(
1000 const detail::argument_name& arg_name,
1001 const detail::argument_name::match_type m_type = detail::argument_name::m_any
1003 return [&arg_name, m_type](
const arg_ptr_t& arg) {
1004 return arg->name().match(arg_name, m_type);
1014 [[nodiscard]]
bool _is_arg_name_used(
1015 const detail::argument_name& arg_name,
1016 const detail::argument_name::match_type m_type = detail::argument_name::m_any
1018 const auto predicate = this->_name_match_predicate(arg_name, m_type);
1020 if (std::ranges::find_if(this->_positional_args, predicate) != this->_positional_args.end())
1023 if (std::ranges::find_if(this->_optional_args, predicate) != this->_optional_args.end())
1034 void _validate_group(
const argument_group& group) {
1035 if (group._parser !=
this)
1036 throw std::logic_error(std::format(
1037 "An argument group '{}' does not belong to the given parser.", group._name
1050 template <util::c_forward_iterator_of<std::
string, util::type_val
idator::convertible> AIt>
1051 void _parse_args_impl(AIt args_begin,
const AIt args_end, parsing_state& state) {
1052 this->_invoked =
true;
1054 if (args_begin != args_end) {
1056 const auto subparser_it =
1057 std::ranges::find(this->_subparsers, *args_begin, [](
const auto& subparser) {
1058 return subparser->_name;
1060 if (subparser_it != this->_subparsers.end()) {
1061 auto& subparser = **subparser_it;
1062 state.set_parser(subparser);
1063 subparser._parse_args_impl(++args_begin, args_end, state);
1069 this->_validate_argument_configuration();
1070 for (
const auto& tok : this->_tokenize(args_begin, args_end, state))
1071 this->_parse_token(tok, state);
1072 this->_verify_final_state();
1073 this->_finalized =
true;
1082 void _validate_argument_configuration()
const {
1084 arg_ptr_t non_required_arg =
nullptr;
1085 for (
const auto& arg : this->_positional_args) {
1086 if (not arg->is_required()) {
1087 non_required_arg = arg;
1091 if (non_required_arg and arg->is_required())
1092 throw invalid_configuration(std::format(
1093 "Required positional argument [{}] cannot be defined after a non-required "
1094 "positional argument [{}].",
1096 non_required_arg->name().str()
1110 template <util::c_forward_iterator_of<std::
string, util::type_val
idator::convertible> AIt>
1111 [[nodiscard]] arg_token_vec_t _tokenize(
1112 AIt args_begin,
const AIt args_end,
const parsing_state& state
1114 arg_token_vec_t toks;
1115 toks.reserve(
static_cast<std::size_t
>(std::ranges::distance(args_begin, args_end)));
1116 std::ranges::for_each(args_begin, args_end, [&](
const auto& arg_value) {
1117 this->_tokenize_arg(arg_value, toks, state);
1129 const std::string_view arg_value, arg_token_vec_t& toks,
const parsing_state& state
1131 detail::argument_token tok{
1132 .type = this->_deduce_token_type(arg_value), .value = std::string(arg_value)
1135 if (not tok.is_flag_token() or this->_validate_flag_token(tok)) {
1136 toks.emplace_back(std::move(tok));
1142 if (state.parse_known_only) {
1143 toks.emplace_back(std::move(tok));
1147 switch (this->_unknown_policy) {
1148 case unknown_policy::fail:
1149 throw parsing_failure::unknown_argument(tok.value);
1150 case unknown_policy::warn:
1151 std::cerr <<
"[argon::warning] Unknown argument '" << tok.value <<
"' will be ignored."
1154 case unknown_policy::ignore:
1156 case unknown_policy::as_values:
1157 tok.type = detail::argument_token::t_value;
1158 toks.emplace_back(std::move(tok));
1172 [[nodiscard]] detail::argument_token::token_type _deduce_token_type(
1173 const std::string_view arg_value
1175 if (util::contains_whitespaces(arg_value))
1176 return detail::argument_token::t_value;
1178 if (arg_value.starts_with(this->_flag_prefix))
1179 return detail::argument_token::t_flag_primary;
1181 if (arg_value.starts_with(this->_flag_prefix_char))
1182 return detail::argument_token::t_flag_secondary;
1184 return detail::argument_token::t_value;
1193 [[nodiscard]]
bool _validate_flag_token(detail::argument_token& tok)
noexcept {
1194 const auto opt_arg_it = this->_find_opt_arg(tok);
1195 if (opt_arg_it == this->_optional_args.end())
1196 return this->_validate_compound_flag_token(tok);
1198 tok.args.emplace_back(*opt_arg_it);
1209 bool _validate_compound_flag_token(detail::argument_token& tok)
noexcept {
1210 if (tok.type != detail::argument_token::t_flag_secondary)
1213 const auto actual_tok_value = this->_strip_flag_prefix(tok);
1214 tok.args.reserve(actual_tok_value.size());
1216 for (
const char c : actual_tok_value) {
1217 const auto opt_arg_it = std::ranges::find_if(
1218 this->_optional_args,
1219 this->_name_match_predicate(
1220 std::string_view(&c, 1ull), detail::argument_name::m_secondary
1224 if (opt_arg_it == this->_optional_args.end()) {
1229 tok.args.emplace_back(*opt_arg_it);
1232 tok.type = detail::argument_token::t_flag_compound;
1242 [[nodiscard]] arg_ptr_vec_iter_t _find_opt_arg(
const detail::argument_token& flag_tok
1244 if (not flag_tok.is_flag_token())
1245 return this->_optional_args.end();
1247 const auto actual_tok_value = this->_strip_flag_prefix(flag_tok);
1248 const auto match_type =
1249 flag_tok.type == detail::argument_token::t_flag_primary
1250 ? detail::argument_name::m_primary
1251 : detail::argument_name::m_secondary;
1253 return std::ranges::find_if(
1254 this->_optional_args, this->_name_match_predicate(actual_tok_value, match_type)
1263 [[nodiscard]] std::string_view _strip_flag_prefix(
const detail::argument_token& tok
1266 case detail::argument_token::t_flag_primary:
1267 return std::string_view(tok.value).substr(this->_primary_flag_prefix_length);
1268 case detail::argument_token::t_flag_secondary:
1269 return std::string_view(tok.value).substr(this->_secondary_flag_prefix_length);
1281 void _parse_token(
const detail::argument_token& tok, parsing_state& state) {
1282 if (state.curr_arg and state.curr_arg->is_greedy()) {
1283 this->_set_argument_value(tok.value, state);
1287 if (tok.is_flag_token())
1288 this->_parse_flag_token(tok, state);
1290 this->_parse_value_token(tok, state);
1299 void _parse_flag_token(
const detail::argument_token& tok, parsing_state& state) {
1300 if (not tok.is_valid_flag_token()) {
1301 if (state.parse_known_only) {
1302 state.curr_arg.reset();
1303 state.unknown_args.emplace_back(tok.value);
1308 throw parsing_failure::unknown_argument(tok.value);
1312 for (
const auto& arg : tok.args) {
1313 if (arg->mark_used())
1314 state.curr_arg = arg;
1316 state.curr_arg.reset();
1326 void _parse_value_token(
const detail::argument_token& tok, parsing_state& state) {
1327 if (not state.curr_arg) {
1328 if (state.curr_pos_arg_it == this->_positional_args.end()) {
1329 state.unknown_args.emplace_back(tok.value);
1333 state.curr_arg = *state.curr_pos_arg_it;
1336 this->_set_argument_value(tok.value, state);
1345 void _set_argument_value(
const std::string_view value, parsing_state& state)
noexcept {
1346 if (state.curr_arg->set_value(std::string(value)))
1350 if (state.curr_arg->is_positional()
1351 and state.curr_pos_arg_it != this->_positional_args.end())
1352 ++state.curr_pos_arg_it;
1354 state.curr_arg.reset();
1361 void _verify_final_state()
const {
1362 const auto [supress_group_checks, suppress_arg_checks] = this->_are_checks_suppressed();
1363 for (
const auto& group : this->_argument_groups)
1364 this->_verify_group_requirements(*group, supress_group_checks, suppress_arg_checks);
1373 [[nodiscard]] std::pair<bool, bool> _are_checks_suppressed() const noexcept {
1374 bool suppress_group_checks =
false;
1375 bool suppress_arg_checks =
false;
1377 auto check_arg = [&](
const arg_ptr_t& arg) {
1378 if (arg->is_used()) {
1379 if (arg->suppresses_group_checks())
1380 suppress_group_checks =
true;
1381 if (arg->suppresses_arg_checks())
1382 suppress_arg_checks =
true;
1387 std::ranges::for_each(this->_positional_args, check_arg);
1388 std::ranges::for_each(this->_optional_args, check_arg);
1389 return {suppress_group_checks, suppress_arg_checks};
1398 void _verify_group_requirements(
1399 const argument_group& group,
1400 const bool suppress_group_checks,
1401 const bool suppress_arg_checks
1403 if (group._arguments.empty())
1406 if (not suppress_group_checks) {
1407 const auto n_used_args =
static_cast<std::size_t
>(std::ranges::count_if(
1408 group._arguments, [](
const auto& arg) { return arg->is_used(); }
1411 if (group._mutually_exclusive) {
1412 if (n_used_args > 1ull)
1413 throw parsing_failure(std::format(
1414 "At most one argument from the mutually exclusive group '{}' can be used",
1418 const auto used_arg_it = std::ranges::find_if(
1419 group._arguments, [](
const auto& arg) { return arg->is_used(); }
1422 if (used_arg_it != group._arguments.end()) {
1424 this->_verify_argument_requirements(*used_arg_it, suppress_arg_checks);
1429 if (group._required and n_used_args == 0ull)
1430 throw parsing_failure(std::format(
1431 "At least one argument from the required group '{}' must be used", group._name
1436 for (
const auto& arg : group._arguments)
1437 this->_verify_argument_requirements(arg, suppress_arg_checks);
1446 void _verify_argument_requirements(
const arg_ptr_t& arg,
const bool suppress_arg_checks)
const {
1447 if (suppress_arg_checks)
1450 if (arg->is_required() and not arg->has_value())
1451 throw parsing_failure(
1452 std::format(
"No values parsed for a required argument [{}]", arg->name().str())
1454 if (
const auto nv_ord = arg->nvalues_ordering(); not std::is_eq(nv_ord))
1455 throw parsing_failure::invalid_nvalues(arg->name(), nv_ord);
1463 arg_ptr_t _get_argument(std::string_view arg_name)
const noexcept {
1464 const auto predicate = this->_name_match_predicate(arg_name);
1466 if (
auto pos_arg_it = std::ranges::find_if(this->_positional_args, predicate);
1467 pos_arg_it != this->_positional_args.end()) {
1471 if (
auto opt_arg_it = std::ranges::find_if(this->_optional_args, predicate);
1472 opt_arg_it != this->_optional_args.end()) {
1479 void _print_subparsers(std::ostream& os)
const noexcept {
1480 if (this->_subparsers.empty())
1483 os <<
"\nCommands:\n";
1485 std::vector<detail::help_builder> builders;
1486 builders.reserve(this->_subparsers.size());
1488 for (
const auto& subparser : this->_subparsers)
1489 builders.emplace_back(subparser->_name, subparser->_program_description);
1491 std::size_t max_subparser_name_length = 0ull;
1492 for (
const auto& bld : builders)
1493 max_subparser_name_length = std::max(max_subparser_name_length, bld.name.length());
1495 for (
const auto& bld : builders)
1496 os <<
'\n' << bld.get_basic(this->_indent_width, max_subparser_name_length);
1508 void _print_group(std::ostream& os,
const argument_group& group,
const bool verbose)
1513 auto visible_args = std::views::filter(group._arguments, [](
const auto& arg) {
1514 return not arg->is_hidden();
1517 if (std::ranges::empty(visible_args))
1520 os <<
'\n' << group._name <<
':';
1522 std::vector<std::string_view> group_attrs;
1523 if (group._required)
1524 group_attrs.emplace_back(
"required");
1525 if (group._mutually_exclusive)
1526 group_attrs.emplace_back(
"mutually exclusive");
1527 if (not group_attrs.empty())
1528 os <<
" (" << util::join(group_attrs) <<
')';
1532 for (
const auto& arg : visible_args)
1533 os <<
'\n' << arg->help_builder(verbose).get(this->_indent_width) <<
'\n';
1536 std::vector<detail::help_builder> builders;
1537 builders.reserve(group._arguments.size());
1539 for (
const auto& arg : visible_args)
1540 builders.emplace_back(arg->help_builder(verbose));
1542 std::size_t max_arg_name_length = 0ull;
1543 for (
const auto& bld : builders)
1544 max_arg_name_length = std::max(max_arg_name_length, bld.name.length());
1546 for (
const auto& bld : builders)
1547 os <<
'\n' << bld.get_basic(this->_indent_width, max_arg_name_length);
1556 std::optional<std::string> _program_version;
1557 std::optional<std::string> _program_description;
1558 bool _verbose =
false;
1561 arg_ptr_vec_t _positional_args;
1562 arg_ptr_vec_t _optional_args;
1563 arg_group_ptr_vec_t _argument_groups;
1564 argument_group& _gr_positional_args;
1565 argument_group& _gr_optional_args;
1566 arg_parser_ptr_vec_t _subparsers;
1570 bool _finalized =
false;
1572 static constexpr std::uint8_t _primary_flag_prefix_length = 2u;
1573 static constexpr std::uint8_t _secondary_flag_prefix_length = 1u;
1574 static constexpr char _flag_prefix_char =
'-';
1575 static constexpr std::string_view _flag_prefix =
"--";
1576 static constexpr std::uint8_t _indent_width = 2;
1593 .help(
"Input file path");
1597 arg_parser.add_positional_argument(
"output").help(
"Output file path");
1601 arg_parser.add_optional_argument<none_type>(
"help",
"h")
1603 .help(
"Display the help message");
1607 arg_parser.add_optional_argument<none_type>(
"version",
"v")
1612 .help(
"Dsiplay program version info");
1616 arg_parser.add_optional_argument(
"input",
"i")
1619 .help(
"Input file path");
1623 arg_parser.add_optional_argument(
"output",
"o").nargs(1ull).help(
"Output file path");
1627 arg_parser.add_optional_argument(
"input",
"i")
1630 .help(
"Input files paths");
1634 arg_parser.add_optional_argument(
"output",
"o")
1636 .help(
"Output files paths");
argon::action_type::on_flag::type print_help(const argument_parser &parser, const std::optional< int > exit_code=std::nullopt, std::ostream &os=std::cout) noexcept
Returns an on-flag action which prints the argument parser's help message.
util::callable_type< argon::action_type::observe, std::string > check_file_exists() noexcept
Returns an observe action which checks whether lower_bound file with the given name exists.
void add_default_argument(const default_argument, argument_parser &) noexcept
@ n_primary
Represents the primary name (used with a long flag prefix –).
constexpr range at_least(const count_type n) noexcept
range class builder function. Creates a range [n, inf).
std::ostream & operator<<(std::ostream &os, const argument_parser &) noexcept
default_argument
The enumeration of default arguments provided by the library.
@ p_output
A positional argument representing a single output file path. Equivalent to:
@ o_help
An optional argument representing the program's help flag. Equivalent to:
@ p_input
A positional argument representing a single input file path. Equivalent to:
@ o_input
A positional argument representing multiple input file paths. Equivalent to:
@ o_multi_input
A positional argument representing multiple input file paths. Equivalent to:
@ o_version
An optional argument representing the program's version flag. Equivalent to:
@ o_output
A positional argument representing multiple output file paths. Equivalent to:
@ o_multi_output
A positional argument representing multiple output file paths. Equivalent to:
unknown_policy
The enumeration of policies for handling unknown arguments.
@ warn
Issue a warning when an unknown argument is encountered.
@ as_values
Treat unknown arguments as positional values.
@ ignore
Ignore unknown arguments.
@ fail
Throw an exception when an unknown argument is encountered.
Provides common ranges utility functions.
Base type for the argument parser functionality errors/exceptions.