28struct argument_parser_test_fixture;
114 template <detail::c_range_of<argument::default_positional> AR>
116 for (
const auto arg_discriminator : arg_discriminator_range)
127 const std::initializer_list<argument::default_positional> arg_discriminator_list
129 return this->default_positional_arguments<>(arg_discriminator_list);
138 template <detail::c_range_of<argument::default_optional> AR>
140 for (
const auto arg_discriminator : arg_discriminator_range)
151 const std::initializer_list<argument::default_optional> arg_discriminator_list
153 return this->default_optional_arguments<>(arg_discriminator_list);
165 template <detail::c_argument_value_type T = std::
string>
187 template <detail::c_argument_value_type T = std::
string>
189 const std::string_view primary_name,
const std::string_view secondary_name
195 std::make_optional<std::string>(primary_name),
196 std::make_optional<std::string>(secondary_name)
215 template <detail::c_argument_value_type T = std::
string>
217 const std::string_view name,
222 const auto arg_name =
223 name_discr == n_primary
225 argument_name{std::make_optional<std::string>(name), std::nullopt, this->
_flag_prefix_char}
247 template <detail::c_argument_value_type T = std::
string>
249 const std::string_view primary_name,
const std::string_view secondary_name
255 std::make_optional<std::string>(primary_name),
256 std::make_optional<std::string>(secondary_name),
273 template <
bool StoreImplicitly = true>
275 const std::string_view name,
278 return this->add_optional_argument<bool>(name, name_discr)
279 .default_value(not StoreImplicitly)
280 .implicit_value(StoreImplicitly)
291 template <
bool StoreImplicitly = true>
293 const std::string_view primary_name,
const std::string_view secondary_name
295 return this->add_optional_argument<bool>(primary_name, secondary_name)
296 .default_value(not StoreImplicitly)
297 .implicit_value(StoreImplicitly)
315 this->
parse_args(std::span(argv + 1,
static_cast<std::size_t
>(argc - 1)));
325 template <detail::c_range_of<std::
string, detail::type_val
idator::convertible> AR>
329 std::vector<std::string> unknown_args;
332 if (not unknown_args.empty())
354 this->
try_parse_args(std::span(argv + 1,
static_cast<std::size_t
>(argc - 1)));
368 template <detail::c_range_of<std::
string, detail::type_val
idator::convertible> AR>
374 std::cerr <<
"[ERROR] : " << err.what() << std::endl << *
this << std::endl;
375 std::exit(EXIT_FAILURE);
398 return this->
parse_known_args(std::span(argv + 1,
static_cast<std::size_t
>(argc - 1)));
414 template <detail::c_range_of<std::
string, detail::type_val
idator::convertible> AR>
418 std::vector<std::string> unknown_args;
458 template <detail::c_range_of<std::
string, detail::type_val
idator::convertible> AR>
464 std::cerr <<
"[ERROR] : " << err.what() << std::endl << *
this << std::endl;
465 std::exit(EXIT_FAILURE);
477 [[deprecated(
"The default help argument now uses the `print_config` on-flag action")]]
479 if (this->value<bool>(
"help")) {
480 std::cout << *
this << std::endl;
481 std::exit(EXIT_SUCCESS);
491 [[nodiscard]]
bool has_value(std::string_view arg_name)
const noexcept {
493 return arg_opt ? arg_opt->get().has_value() :
false;
500 [[nodiscard]] std::size_t
count(std::string_view arg_name)
const noexcept {
502 return arg_opt ? arg_opt->get().count() : 0ull;
511 template <detail::c_argument_value_type T = std::
string>
512 [[nodiscard]] T
value(std::string_view arg_name)
const {
517 const auto& arg_value = arg_opt->get().value();
519 return std::any_cast<T>(arg_value);
521 catch (
const std::bad_any_cast&) {
522 throw type_error::invalid_value_type<T>(arg_opt->get().name());
534 template <detail::c_argument_value_type T = std::
string, std::convertible_to<T> U>
535 [[nodiscard]] T
value_or(std::string_view arg_name, U&& fallback_value)
const {
541 const auto& arg_value = arg_opt->get().value();
542 return std::any_cast<T>(arg_value);
544 catch (
const std::logic_error&) {
547 return T{std::forward<U>(fallback_value)};
549 catch (
const std::bad_any_cast&) {
550 throw type_error::invalid_value_type<T>(arg_opt->get().name());
560 template <detail::c_argument_value_type T = std::
string>
561 [[nodiscard]] std::vector<T>
values(std::string_view arg_name)
const {
566 const auto& arg = arg_opt->get();
569 if (not arg.has_parsed_values() and arg.has_value())
570 return std::vector<T>{std::any_cast<T>(arg.value())};
575 std::views::transform(
576 arg.values(), [](
const std::any&
value) { return std::any_cast<T>(value); }
578 std::back_inserter(
values)
582 catch (
const std::bad_any_cast&) {
583 throw type_error::invalid_value_type<T>(arg.name());
606 os <<
"\nPositional arguments:\n";
611 os <<
"\nOptional arguments:\n";
627 parser.print_config(parser._verbose, os);
633 friend struct ::ap_testing::argument_parser_test_fixture;
637 using arg_ptr_t = std::unique_ptr<detail::argument_base>;
641 using arg_opt_t = std::optional<std::reference_wrapper<detail::argument_base>>;
642 using const_arg_opt_t = std::optional<std::reference_wrapper<const detail::argument_base>>;
652 if (arg_name.empty())
654 arg_name,
"An argument name cannot be empty."
659 arg_name,
"An argument name cannot contain whitespaces."
662 if (arg_name.front() == this->_flag_prefix_char)
666 "An argument name cannot begin with a flag prefix character ({}).",
671 if (std::isdigit(arg_name.front()))
673 arg_name,
"An argument name cannot begin with a digit."
684 const std::string_view arg_name,
687 return [=](
const arg_ptr_t& arg) {
return arg->name().match(arg_name, m_type); };
700 return [&arg_name, m_type](
const arg_ptr_t& arg) {
701 return arg->name().match(arg_name, m_type);
736 if (not arg->is_required()) {
737 non_required_arg = std::ref(*arg);
741 if (non_required_arg and arg->is_required())
743 arg->name(), non_required_arg->get().name()
754 template <detail::c_sized_range_of<std::
string_view, detail::type_val
idator::convertible> AR>
756 const auto n_args = std::ranges::size(arg_range);
761 toks.reserve(n_args);
762 std::ranges::for_each(
776 if (not tok.is_flag_token() or this->_validate_flag_token(tok)) {
777 toks.emplace_back(std::move(tok));
783 if (not compound_toks.empty()) {
784 toks.insert(toks.end(), compound_toks.begin(), compound_toks.end());
788#ifdef AP_UNKNOWN_FLAGS_AS_VALUES
791 toks.emplace_back(std::move(tok));
805 if (arg_value.starts_with(this->_flag_prefix))
808 .value = std::string(arg_value.substr(this->_primary_flag_prefix_length))
811 if (arg_value.starts_with(this->_flag_prefix_char))
814 .value = std::string(arg_value.substr(this->_secondary_flag_prefix_length))
831 tok.arg.emplace(*opt_arg_it);
844 std::vector<detail::argument_token> compound_toks;
845 compound_toks.reserve(tok.value.size());
848 return compound_toks;
850 for (
const char c : tok.value) {
855 compound_toks.clear();
856 return compound_toks;
858 compound_toks.emplace_back(std::move(ctok));
861 return compound_toks;
880 return std::format(
"{}{}", this->
_flag_prefix, tok.value);
896 std::vector<std::string>& unknown_args,
897 const bool handle_unknown =
true
913 if (token_it == tokens_end)
919 pos_arg->set_value(token_it->value);
934 std::vector<std::string>& unknown_args,
935 const bool handle_unknown =
true
939 while (token_it != tokens_end) {
940 switch (token_it->type) {
944 if (not token_it->is_valid_flag_token()) {
945 if (handle_unknown) {
952 curr_opt_arg.reset();
957 if (token_it->arg->get()->mark_used())
958 curr_opt_arg = token_it->arg;
960 curr_opt_arg.reset();
965 if (not curr_opt_arg) {
966 unknown_args.emplace_back(token_it->value);
970 if (not curr_opt_arg->get()->set_value(token_it->value))
971 curr_opt_arg.reset();
987 return std::ranges::any_of(
990 return arg->is_used() and arg->bypass_required_enabled();
994 return arg->is_used() and arg->bypass_required_enabled();
1005 if (arg->is_required() and not arg->has_value())
1009 if (arg->is_required() and not arg->has_value())
1020 if (
const auto nv_ord = arg->nvalues_ordering(); not std::is_eq(nv_ord))
1024 if (
const auto nv_ord = arg->nvalues_ordering(); not std::is_eq(nv_ord))
1036 if (
auto pos_arg_it = std::ranges::find_if(this->
_positional_args, predicate);
1038 return std::ref(**pos_arg_it);
1041 if (
auto opt_arg_it = std::ranges::find_if(this->
_optional_args, predicate);
1043 return std::ref(**opt_arg_it);
1046 return std::nullopt;
1057 switch (flag_tok.type) {
1059 return std::ranges::find(this->
_optional_args, flag_tok.value, [](
const auto& arg_ptr) {
1060 return arg_ptr->name().primary;
1063 return std::ranges::find(this->
_optional_args, flag_tok.value, [](
const auto& arg_ptr) {
1064 return arg_ptr->name().secondary;
1078 std::views::filter(args, [](
const auto& arg) {
return not arg->is_hidden(); });
1081 for (
const auto& arg : visible_args)
1085 std::vector<detail::argument_descriptor> descriptors;
1086 descriptors.reserve(args.size());
1088 for (
const auto& arg : visible_args)
1089 descriptors.emplace_back(arg->desc(
verbose));
1091 std::size_t max_arg_name_length = 0ull;
1092 for (
const auto& desc : descriptors)
1093 max_arg_name_length = std::max(max_arg_name_length, desc.name.length());
1095 for (
const auto& desc : descriptors)
1096 os <<
'\n' << desc.get_basic(this->_indent_width, max_arg_name_length);
1127 switch (arg_discriminator) {
1129 arg_parser.add_positional_argument(
"input")
1131 .help(
"Input file path");
1135 arg_parser.add_positional_argument(
"output").help(
"Output file path");
1148 switch (arg_discriminator) {
1150 arg_parser.add_flag(
"help",
"h")
1153 .help(
"Display the help message");
1157 arg_parser.add_optional_argument(
"input",
"i")
1161 .help(
"Input file path");
1165 arg_parser.add_optional_argument(
"output",
"o").required().nargs(1).help(
"Output file path");
1169 arg_parser.add_optional_argument(
"input",
"i")
1173 .help(
"Input files paths");
1177 arg_parser.add_optional_argument(
"output",
"o")
1180 .help(
"Output files paths");
The optioanl argument class.
The positional argument class.
Main argument parser class.
argument_parser & program_version(const version &version) noexcept
Set the program version.
argument_parser & default_positional_arguments(const AR &arg_discriminator_range) noexcept
Set default positional arguments.
std::vector< std::string > try_parse_known_args(int argc, char *argv[])
Parses the known command-line arguments and exits on error.
argument::optional< bool > & add_flag(const std::string_view name, const detail::argument_name_discriminator name_discr=n_primary)
Adds a boolean flag argument (an optional argument with value_type = bool) to the parser's configurat...
void try_parse_args(const AR &argv_rng)
Parses the command-line arguments and exits on error.
detail::argument_token _build_token(const std::string_view arg_value) const noexcept
Builds an argument token from the given value.
static constexpr uint8_t _indent_width
argument_parser & default_positional_arguments(const std::initializer_list< argument::default_positional > arg_discriminator_list) noexcept
Set default positional arguments.
argument::optional< T > & add_optional_argument(const std::string_view name, const detail::argument_name_discriminator name_discr=n_primary)
Adds a positional argument to the parser's configuration.
typename arg_ptr_list_t::iterator arg_ptr_list_iter_t
void _verify_nvalues() const
Check if the number of argument values is within the specified range.
~argument_parser()=default
std::optional< std::reference_wrapper< detail::argument_base > > arg_opt_t
std::optional< std::string > _program_description
bool _validate_flag_token(detail::argument_token &tok) noexcept
Check if a flag token is valid based on its value.
std::string _unstripped_token_value(const detail::argument_token &tok) const noexcept
Get the unstripped token value (including the flag prefix).
std::vector< arg_ptr_t > arg_ptr_list_t
argument_parser(const argument_parser &)=delete
void _verify_arg_name_pattern(const std::string_view arg_name) const
Verifies the pattern of an argument name and if it's invalid, an error is thrown.
argument_parser & program_version(std::string_view version) noexcept
Set the program version.
void print_config(const bool verbose, std::ostream &os=std::cout) const noexcept
Prints the argument parser's details to an output stream.
bool _is_arg_name_used(const detail::argument_name &arg_name, const detail::argument_name::match_type m_type=detail::argument_name::m_any) const noexcept
Check if an argument name is already used.
argument_parser(argument_parser &&)=default
arg_ptr_list_iter_t _find_opt_arg(const detail::argument_token &flag_tok) noexcept
Find an optional argument based on a flag token.
argument_parser & program_description(std::string_view description) noexcept
Set the program description.
T value(std::string_view arg_name) const
auto _name_match_predicate(const detail::argument_name &arg_name, const detail::argument_name::match_type m_type=detail::argument_name::m_any) const noexcept
Returns a unary predicate function which checks if the given name matches the argument's name.
argument::positional< T > & add_positional_argument(const std::string_view primary_name, const std::string_view secondary_name)
Adds a positional argument to the parser's configuration.
argument_parser & program_name(std::string_view name) noexcept
Set the program name.
static constexpr char _flag_prefix_char
void _validate_argument_configuration() const
Validate whether the definition/configuration of the parser's arguments is correct.
T value_or(std::string_view arg_name, U &&fallback_value) const
bool has_value(std::string_view arg_name) const noexcept
std::optional< std::reference_wrapper< const detail::argument_base > > const_arg_opt_t
argument_parser & operator=(const argument_parser &)=delete
std::vector< detail::argument_token > arg_token_list_t
void _parse_args_impl(const arg_token_list_t &arg_tokens, std::vector< std::string > &unknown_args, const bool handle_unknown=true)
Implementation of parsing command-line arguments.
std::vector< detail::argument_token > _try_split_compound_flag(const detail::argument_token &tok) noexcept
Tries to split a secondary flag token into separate flag token (one for each character of the token's...
std::vector< T > values(std::string_view arg_name) const
detail::uptr_opt_t< detail::argument_base > arg_ptr_opt_t
typename arg_token_list_t::const_iterator arg_token_list_iterator_t
static constexpr uint8_t _secondary_flag_prefix_length
std::vector< std::string > parse_known_args(const AR &argv_rng)
Parses the known command-line arguments.
argument::optional< bool > & add_flag(const std::string_view primary_name, const std::string_view secondary_name)
Adds a boolean flag argument (an optional argument with value_type = bool) to the parser's configurat...
arg_ptr_list_t _optional_args
argument_parser & operator=(argument_parser &&)=default
std::optional< std::string > _program_name
std::vector< std::string > parse_known_args(int argc, char *argv[])
Parses the known command-line arguments.
argument_parser & verbose(const bool v=true) noexcept
Set the verbosity mode.
argument_parser & default_optional_arguments(const AR &arg_discriminator_range) noexcept
Set default optional arguments.
static constexpr std::string_view _flag_prefix
auto _name_match_predicate(const std::string_view arg_name, const detail::argument_name::match_type m_type=detail::argument_name::m_any) const noexcept
Returns a unary predicate function which checks if the given name matches the argument's name.
void try_parse_args(int argc, char *argv[])
Parses the command-line arguments and exits on error.
void handle_help_action() const noexcept
Handles the help argument logic.
arg_opt_t _get_argument(std::string_view arg_name) const noexcept
Get the argument with the specified name.
argument::positional< T > & add_positional_argument(const std::string_view primary_name)
Adds a positional argument to the parser's configuration.
void _parse_positional_args(arg_token_list_iterator_t &token_it, const arg_token_list_iterator_t &tokens_end) noexcept
Parse positional arguments based on command-line input.
std::unique_ptr< detail::argument_base > arg_ptr_t
void _verify_required_args() const
Check if all required positional and optional arguments are used.
void _tokenize_arg(arg_token_list_t &toks, const std::string_view arg_value)
Appends an argument token(s) created from arg_value to the toks vector.
argument_parser & default_optional_arguments(const std::initializer_list< argument::default_optional > arg_discriminator_list) noexcept
Set default optional arguments.
std::vector< std::string > try_parse_known_args(const AR &argv_rng)
Parses known the command-line arguments and exits on error.
static constexpr uint8_t _primary_flag_prefix_length
arg_token_list_t _tokenize(const AR &arg_range)
Converts the command-line arguments into a list of tokens.
std::size_t count(std::string_view arg_name) const noexcept
argument_parser()=default
void parse_args(const AR &argv_rng)
Parses the command-line arguments.
friend std::ostream & operator<<(std::ostream &os, const argument_parser &parser) noexcept
Prints the argument parser's details to an output stream.
std::optional< std::string > _program_version
void parse_args(int argc, char *argv[])
Parses the command-line arguments.
void _print(std::ostream &os, const arg_ptr_list_t &args, const bool verbose) const noexcept
Print the given argument list to an output stream.
argument::optional< T > & add_optional_argument(const std::string_view primary_name, const std::string_view secondary_name)
Adds a positional argument to the parser's configuration.
void _parse_optional_args(arg_token_list_iterator_t &token_it, const arg_token_list_iterator_t &tokens_end, std::vector< std::string > &unknown_args, const bool handle_unknown=true)
Parse optional arguments based on command-line input.
bool _are_required_args_bypassed() const noexcept
Check whether required argument bypassing is enabled.
arg_ptr_list_t _positional_args
Provides the general concept definitions.
Defines the default argument discriminator types.
ap::action_type::on_flag::type print_config(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 configuration.
detail::callable_type< ap::action_type::observe, std::string > check_file_exists() noexcept
Returns an observe action which checks whether lower_bound file with the given name exists.
default_positional
Enum class representing positional arguments.
default_optional
Enum class representing optional arguments.
void add_default_argument(const argument::default_positional, argument_parser &) noexcept
Adds a predefined/default positional argument to the parser.
bool contains_whitespaces(std::string_view str) noexcept
Checks whether a string contains any whitespace characters.
argument_name_discriminator
Argument name member discriminator.
std::optional< std::reference_wrapper< std::unique_ptr< T > > > uptr_opt_t
range at_least(const range::count_type n) noexcept
range class builder function. Creates a range [n, inf].
An observing value action specifier.
An on-flag action specifier.
Base type for the argument parser functionality errors/exceptions.
Structure holding the argument's name.
match_type
Specifies the type of argument name match.
@ m_any
Matches either the primary or the secondary name.
Structure representing a single command-line argument token.
token_type type
The token's type discrimiator value.
@ t_value
Represents a value argument.
@ t_flag_secondary
Represents the secondary (-) flag argument.
@ t_flag_primary
Represents the primary (–) flag argument.
static invalid_configuration required_after_non_required(const detail::argument_name &required_arg_name, const detail::argument_name &non_required_arg_name) noexcept
static invalid_configuration argument_name_used(const detail::argument_name &arg_name) noexcept
static invalid_configuration invalid_argument_name(const std::string_view arg_name, const std::string_view reason) noexcept
static lookup_failure argument_not_found(const std::string_view &arg_name) noexcept
static parsing_failure invalid_nvalues(const detail::argument_name &arg_name, const std::weak_ordering ordering) noexcept
static parsing_failure unknown_argument(const std::string_view arg_name) noexcept
static parsing_failure required_argument_not_parsed(const detail::argument_name &arg_name) noexcept
static parsing_failure argument_deduction_failure(const std::vector< std::string > &values) noexcept
A helper structure used to represent a program's version.
std::string str() const noexcept
Converts the structure into a string in the v{major}.{minor}.{path} format.