1 #ifndef OOKII_COMMAND_LINE_PARSER_H_
7 #define OOKII_COMMAND_LINE_PARSER_H_
27 template<
typename CharType,
typename Traits,
typename Alloc>
30 using string_type = std::basic_string<CharType, Traits, Alloc>;
31 using string_provider_type = basic_localized_string_provider<CharType, Traits, Alloc>;
33 parser_storage(string_type command_name,
const string_provider_type *string_provider)
34 : command_name{command_name},
35 string_provider{string_provider}
37 if (this->string_provider ==
nullptr)
43 string_type command_name;
44 string_type description;
45 std::vector<string_type> prefixes;
46 string_type long_prefix;
48 const string_provider_type *string_provider;
50 CharType argument_value_separator{
':'};
52 bool allow_white_space_separator{
true};
53 bool allow_duplicate_arguments{
false};
56 template<
typename CharType,
typename Traits,
typename Alloc>
57 struct creation_options
59 using string_type = std::basic_string<CharType, Traits, Alloc>;
61 bool case_sensitive{};
62 bool automatic_help_argument{
true};
104 template<
typename CharType = details::default_
char_type,
typename Traits = std::
char_traits<CharType>,
typename Alloc = std::allocator<CharType>>
146 std::vector<string_type> result;
154 constexpr
auto prefix1 = literal_cast<CharType>(
"-");
155 result.push_back(prefix1.data());
158 constexpr
auto prefix2 = literal_cast<CharType>(
"/");
159 result.push_back(prefix2.data());
175 template<
typename Range>
177 : _storage{std::move(storage)},
179 _arguments_by_short_name{
char_less{options.case_sensitive, _storage.
locale}}
181 for (
const auto &argument_builder :
arguments)
183 add_argument(argument_builder->to_argument(*
this));
186 add_automatic_help_argument(options);
189 std::sort(_arguments.begin(), _arguments.end(),
190 [
this](
const auto &left,
const auto &right)
194 if (left->position())
196 return right->position()
197 ? *left->position() < *right->position()
200 else if (right->position())
206 if (left->is_required())
208 if (!right->is_required())
213 else if (right->is_required())
218 return _arguments_by_name.key_comp()(left->name(), right->name());
224 _sorted_prefixes.push_back(prefix_info { _storage.long_prefix,
false });
227 std::transform(_storage.prefixes.begin(), _storage.prefixes.end(), std::back_inserter(_sorted_prefixes),
228 [
this](
const auto &prefix)
230 return prefix_info{ prefix, _storage.mode == parsing_mode::long_short };
233 std::sort(_sorted_prefixes.begin(), _sorted_prefixes.end(),
234 [](
const auto &left,
const auto &right)
236 return left.prefix.size() > right.prefix.size();
245 return _storage.mode;
253 return _storage.command_name;
261 return _storage.description;
269 return _storage.allow_white_space_separator;
277 return _storage.allow_duplicate_arguments;
285 return _storage.argument_value_separator;
293 const std::vector<string_type> &
prefixes() const noexcept
295 return _storage.prefixes;
305 return _storage.long_prefix;
311 const std::locale &
locale() const noexcept
313 return _storage.locale;
321 return *_storage.string_provider;
329 _help_requested = help_requested;
344 return _help_requested;
352 return details::range_filter<const argument_base_type&, typename std::vector<std::unique_ptr<argument_base_type>>::const_iterator>{
355 [](
const auto &a) ->
auto&
366 return _arguments.size();
375 return _arguments_by_name.key_comp();
383 return _arguments_by_short_name.key_comp();
391 return _positional_argument_count;
405 if (pos >= _positional_argument_count)
407 throw std::out_of_range(
"pos");
410 return *_arguments.at(pos);
424 if (pos >= _positional_argument_count)
426 throw std::out_of_range(
"pos");
429 return *_arguments.at(pos);
440 auto it = this->_arguments_by_name.find(name);
441 if (it == this->_arguments_by_name.end())
455 auto it = this->_arguments_by_name.find(name);
456 if (it == this->_arguments_by_name.end())
470 auto it = this->_arguments_by_short_name.find(name);
471 if (it == this->_arguments_by_short_name.end())
485 auto it = this->_arguments_by_short_name.find(name);
486 if (it == this->_arguments_by_short_name.end())
498 return _help_argument;
511 template<
typename Iterator>
514 help_requested(
false);
515 for (
auto &arg : _arguments)
519 for (
auto current = begin; current != end; ++current)
522 auto prefix = check_prefix(arg);
525 auto [without_prefix, is_short] = *prefix;
528 auto result = parse_named_argument(without_prefix, is_short, current, end);
538 while (position < _positional_argument_count &&
539 !_arguments[position]->is_multi_value() &&
540 _arguments[position]->has_value())
545 if (position >= _positional_argument_count)
547 return create_result(parse_error::too_many_arguments);
550 auto result = set_argument_value(*_arguments[position], arg);
558 for (
const auto &arg : _arguments)
560 if (arg->is_required())
562 if (!arg->has_value())
564 return create_result(parse_error::missing_required_argument, arg->name());
569 arg->apply_default_value();
573 help_requested(
false);
574 return create_result(parse_error::none);
590 template<
typename Iterator>
593 auto result = parse(begin, end);
594 handle_error(result, usage);
607 template<
typename Range>
613 return parse(begin(range), end(range));
628 template<
typename Range>
631 auto result = parse(range);
632 handle_error(result, usage);
648 return parse(args.begin(), args.end());
666 auto result = parse(args);
667 handle_error(result, usage);
683 return parse<const CharType *>({});
685 return parse(argv + 1, argv + argc);
702 auto result = parse(argc, argv);
703 handle_error(result, usage);
716 if (usage ==
nullptr)
722 usage->write_parser_usage(*
this, request);
742 _on_parsed_callback = callback;
761 std::filesystem::path path{argv[0]};
762 if (include_extension)
764 path = path.filename();
771 return path.string<CharType, Traits, Alloc>();
781 void add_argument(std::unique_ptr<argument_base_type> argument)
783 if (argument->has_long_name())
785 auto name = argument->name();
786 const auto [it, success] = _arguments_by_name.insert(std::pair{name, argument.get()});
788 throw std::logic_error(
"Duplicate argument name.");
790 for (
const auto &alias : argument->aliases())
792 const auto [alias_it, alias_success] = _arguments_by_name.insert(std::pair{alias, argument.get()});
794 throw std::logic_error(
"Duplicate argument name.");
798 if (argument->has_short_name())
800 auto name = argument->short_name();
801 const auto [it,
success] = _arguments_by_short_name.insert(std::pair{name, argument.get()});
803 throw std::logic_error(
"Duplicate short argument name.");
805 for (
const auto &alias : argument->short_aliases())
807 const auto [alias_it, alias_success] = _arguments_by_short_name.insert(std::pair{alias, argument.get()});
809 throw std::logic_error(
"Duplicate short argument name.");
814 if (argument->position())
816 ++_positional_argument_count;
819 _arguments.push_back(std::move(argument));
822 void add_automatic_help_argument(creation_options_type &options)
824 if (!options.automatic_help_argument)
829 auto name = _storage.string_provider->automatic_help_name();
832 if (!_arguments.empty())
834 if (std::isupper(_arguments[0]->name()[0], _storage.locale))
836 name[0] = std::toupper(name[0], _storage.locale);
840 name[0] = std::tolower(name[0], _storage.locale);
844 auto short_name = _storage.string_provider->automatic_help_short_name();
845 auto short_alias = std::tolower(name[0], _storage.locale);
846 const auto *existing_arg = get_argument(name);
847 if (existing_arg ==
nullptr)
849 if (_storage.mode == parsing_mode::long_short)
851 existing_arg = get_short_argument(short_name);
852 if (existing_arg ==
nullptr)
854 existing_arg = get_short_argument(short_alias);
859 existing_arg = get_argument(string_view_type{&short_name, 1});
860 if (existing_arg ==
nullptr)
862 existing_arg = get_argument(string_view_type{&short_alias, 1});
867 if (existing_arg !=
nullptr)
869 _help_argument = existing_arg;
874 if (options.case_sensitive)
876 has_alias = short_name != short_alias;
880 has_alias = std::toupper(short_name, _storage.locale) != std::toupper(short_alias, _storage.locale);
883 details::argument_storage<CharType, Traits, Alloc> storage{name};
884 if (_storage.mode == parsing_mode::long_short)
886 storage.short_name = short_name;
889 storage.short_aliases.push_back(short_alias);
895 storage.aliases.push_back(string_type(1, short_name));
898 storage.aliases.push_back(string_type(1, short_alias));
902 storage.cancel_parsing =
true;
903 storage.description = _storage.string_provider->automatic_help_description();
904 storage.value_description = value_description<bool, CharType, Traits, Alloc>::get();
906 details::action_argument_storage<bool, CharType, Traits, Alloc> action_storage{automatic_help_handler};
907 auto argument = std::make_unique<action_command_line_argument<bool, CharType, Traits, Alloc>>(
908 *
this, std::move(storage), std::move(action_storage));
910 _help_argument = argument.get();
911 add_argument(std::move(argument));
914 static bool automatic_help_handler(
bool, basic_command_line_parser &)
919 void handle_error(
const result_type &result, usage_writer_type *usage)
923 auto request = usage_help_request::full;
926 if (result.error != parse_error::parsing_cancelled)
928 request = _storage.show_usage_on_error;
929 if (usage ==
nullptr)
931 usage_writer_type{}.write_error(result.get_error_message());
935 usage->write_error(result.get_error_message());
939 if (help_requested())
941 write_usage(usage, request);
946 std::optional<std::tuple<string_view_type, bool>> check_prefix(string_view_type argument)
const
950 if (argument.length() >= 2 && argument[0] ==
'-' && std::isdigit(argument[1], _storage.locale))
953 for (
const auto &prefix : _sorted_prefixes)
955 auto stripped =
strip_prefix(argument, string_view_type{prefix.prefix});
958 return make_tuple(*stripped, prefix.is_short);
965 template<
typename Iterator>
966 result_type parse_named_argument(string_view_type arg_string,
bool is_short, Iterator ¤t, Iterator end)
968 auto [name, value] =
split_once(arg_string, _storage.argument_value_separator);
969 if (is_short && name.length() > 1)
971 return parse_combined_short_argument(name, value);
974 auto arg = find_argument(name, is_short);
977 return create_result(parse_error::unknown_argument, string_type{name});
980 if (!value && !arg->is_switch())
982 auto value_it = current;
983 if (!_storage.allow_white_space_separator || ++value_it == end || check_prefix(*value_it))
985 return create_result(parse_error::missing_value, arg->name());
992 return set_argument_value(*arg, value);
995 result_type parse_combined_short_argument(string_view_type name, std::optional<string_view_type> value)
997 for (CharType ch : name)
999 auto arg = get_short_argument(ch);
1002 return create_result(parse_error::unknown_argument, string_type{ch});
1005 if (!arg->is_switch())
1007 return create_result(parse_error::combined_short_name_non_switch, string_type{name});
1010 auto result = set_argument_value(*arg, value);
1017 return create_result(parse_error::none);
1020 argument_base_type *find_argument(string_view_type name,
bool is_short) noexcept
1024 assert(name.length() == 1);
1025 return get_short_argument(name[0]);
1028 return get_argument(name);
1031 result_type set_argument_value(argument_base_type &arg, std::optional<string_view_type> value)
1033 if (!_storage.allow_duplicate_arguments && !arg.is_multi_value() && arg.has_value())
1034 return create_result(parse_error::duplicate_argument, arg.name());
1039 assert(arg.is_switch());
1040 result = arg.set_switch_value(*
this);
1044 result = arg.set_value(*value, *
this);
1045 if (result == set_value_result::error)
1047 return create_result(parse_error::invalid_value, arg.name());
1051 return post_process_argument(arg, value, result);
1054 result_type post_process_argument(argument_base_type &arg, std::optional<string_view_type> value,
set_value_result result)
1056 auto action = on_parsed_action::none;
1057 if (_on_parsed_callback)
1058 action = _on_parsed_callback(arg, value);
1060 if (action == on_parsed_action::cancel_parsing ||
1061 ((arg.cancel_parsing() || result == set_value_result::cancel) && action != on_parsed_action::always_continue))
1064 if (action == on_parsed_action::cancel_parsing || arg.cancel_parsing())
1066 help_requested(
true);
1069 return create_result(parse_error::parsing_cancelled, arg.name());
1072 return create_result(parse_error::none);
1075 result_type create_result(
parse_error error, string_type arg_name = {})
1077 if (error != parse_error::none && error != parse_error::parsing_cancelled)
1079 help_requested(
true);
1082 return {*_storage.string_provider,
error, arg_name};
1085 storage_type _storage;
1086 std::vector<prefix_info> _sorted_prefixes;
1090 std::vector<std::unique_ptr<argument_base_type>> _arguments;
1091 std::map<string_type, argument_base_type *, string_less> _arguments_by_name;
1092 std::map<CharType, argument_base_type *, char_less> _arguments_by_short_name;
1093 size_t _positional_argument_count{};
1094 on_parsed_callback _on_parsed_callback;
1095 const argument_base_type* _help_argument{};
1096 bool _help_requested{};
Parses command line arguments into strongly-typed values.
Definition: command_line_parser.h:106
void write_usage(usage_writer_type *usage=nullptr, usage_help_request request=usage_help_request::full)
Writes usage help for this parser's arguments.
Definition: command_line_parser.h:714
auto short_argument_comparer() const
Gets the comparer used for short argument names.
Definition: command_line_parser.h:381
result_type parse(int argc, const CharType *const argv[])
Parses the provided arguments.
Definition: command_line_parser.h:680
const argument_base_type * get_help_argument() const noexcept
Gets the help argument, if there is one.
Definition: command_line_parser.h:496
const string_provider_type & string_provider() const noexcept
Gets the basic_localized_string_provider implementation used to get strings for error messages etc.
Definition: command_line_parser.h:319
CharType argument_value_separator() const noexcept
Definition: command_line_parser.h:283
result_type parse(std::initializer_list< T > args)
Parses the arguments in the specified initializer list.
Definition: command_line_parser.h:646
const argument_base_type & get_argument(size_t pos) const
Gets an argument by position.
Definition: command_line_parser.h:403
parsing_mode mode() const noexcept
Gets the parsing mode used by this parser.
Definition: command_line_parser.h:243
void on_parsed(on_parsed_callback callback)
Sets a callback that will be invoked every time an argument is parsed.
Definition: command_line_parser.h:740
argument_base_type & get_argument(size_t pos)
Gets an argument by position.
Definition: command_line_parser.h:422
static string_type get_executable_name(int argc, const CharType *const argv[], bool include_extension=false)
Extracts the executable name from the application's arguments.
Definition: command_line_parser.h:754
typename argument_type< T >::typed_storage_type::converter_type converter_type
The specialized type of the custom command line converter function used.
Definition: command_line_parser.h:137
const argument_base_type * get_short_argument(CharType name) const noexcept
Gets an argument by short name.
Definition: command_line_parser.h:468
const string_type & command_name() const noexcept
Returns the command name used when generating usage help.
Definition: command_line_parser.h:251
argument_base_type * get_argument(string_view_type name) noexcept
Gets an argument by name.
Definition: command_line_parser.h:453
static constexpr std::vector< string_type > get_default_prefixes()
Gets the default prefixes accepted by the parser.
Definition: command_line_parser.h:144
const string_type & description() const noexcept
Returns the description used when generating usage help.
Definition: command_line_parser.h:259
size_t positional_argument_count() const
Gets the number of positional arguments.
Definition: command_line_parser.h:389
bool help_requested() const noexcept
Gets a value that indicates help should be shown if parse() returns a parse_result with parse_error::...
Definition: command_line_parser.h:342
const std::vector< string_type > & prefixes() const noexcept
Gets a list of all the argument name prefixes accepted by the parser.
Definition: command_line_parser.h:293
const std::locale & locale() const noexcept
Gets the locale used to parse argument values and to format strings.
Definition: command_line_parser.h:311
bool allow_duplicate_arguments() const noexcept
Indicates whether duplicate arguments are allowed.
Definition: command_line_parser.h:275
const string_type & long_prefix() const noexcept
Gets the long argument prefix.
Definition: command_line_parser.h:303
details::parser_storage< CharType, Traits, Alloc > storage_type
The specialized type of parser parameter storage used. For internal use.
Definition: command_line_parser.h:119
result_type parse(const Range &range)
Parses the arguments in the specified range.
Definition: command_line_parser.h:608
bool allow_white_space_separator() const noexcept
Indicates whether argument names and values can be separated by white space.
Definition: command_line_parser.h:267
std::basic_string_view< CharType, Traits > string_view_type
The specialized type of std::basic_string_view used.
Definition: command_line_parser.h:113
auto arguments() const
Gets a view of all the arguments defined by the parser.
Definition: command_line_parser.h:350
result_type parse(std::initializer_list< T > args, usage_writer_type *usage)
Parses the arguments in the specified initializer list, and writes error and usage information to the...
Definition: command_line_parser.h:664
result_type parse(Range range, usage_writer_type *usage)
Parses the arguments in the specified range, and writes error and usage information to the console if...
Definition: command_line_parser.h:629
details::creation_options< CharType, Traits, Alloc > creation_options_type
The specialized type of parser creation options used. For internal use.
Definition: command_line_parser.h:121
auto argument_comparer() const
Gets the comparer used for argument names.
Definition: command_line_parser.h:373
std::function< on_parsed_action(argument_base_type &, std::optional< string_view_type > value)> on_parsed_callback
The callback function type for on_parsed().
Definition: command_line_parser.h:123
argument_base_type * get_short_argument(CharType name) noexcept
Gets an argument by short name.
Definition: command_line_parser.h:483
const argument_base_type * get_argument(string_view_type name) const noexcept
Gets an argument by name.
Definition: command_line_parser.h:438
basic_command_line_parser(const Range &arguments, storage_type &&storage, creation_options_type &options)
Creates a new instance of the basic_command_line_parser class.
Definition: command_line_parser.h:176
size_t argument_count() const
Gets the total number of arguments.
Definition: command_line_parser.h:364
result_type parse(int argc, const CharType *const argv[], usage_writer_type *usage)
Parses the provided arguments, and writes error and usage information to the console if a parsing err...
Definition: command_line_parser.h:700
typename argument_base_type::string_type string_type
The specialized type of std::basic_string used.
Definition: command_line_parser.h:111
result_type parse(Iterator begin, Iterator end, usage_writer_type *usage)
Parses the arguments in the range specified by the iterators, and writes error and usage information ...
Definition: command_line_parser.h:591
void help_requested(bool help_requested) noexcept
Sets a value that indicates help should be shown if parse() returns a parse_result with parse_error::...
Definition: command_line_parser.h:327
result_type parse(Iterator begin, Iterator end)
Parses the arguments in the range specified by the iterators.
Definition: command_line_parser.h:512
Provides custom localized strings.
Definition: localized_string_provider.h:30
static basic_localized_string_provider & get_default() noexcept
Gets a default instance of the basic_localized_string_provider.
Definition: localized_string_provider.h:38
Creates usage help for the basic_command_line_parser and basic_command_manager classes.
Definition: usage_writer.h:99
virtual void write_parser_usage(const parser_type &parser, usage_help_request request=usage_help_request::full)
Creates usage help for the specified parser.
Definition: usage_writer.h:368
Abstract base class for regular and multi-value arguments.
Definition: command_line_argument.h:108
typename storage_type::string_type string_type
The concrete type of std::basic_string used.
Definition: command_line_argument.h:113
Class that provides information about arguments that are not multi-value arguments.
Definition: command_line_argument.h:432
Provides the ookii::command_line_argument class.
Namespace containing the core Ookii.CommandLine.Cpp types.
Definition: command_line_argument.h:16
usage_help_request
Indicates if and how usage is shown if an error occurred parsing the command line.
Definition: usage_writer.h:26
std::optional< std::basic_string_view< CharType, Traits > > strip_prefix(std::basic_string_view< CharType, Traits > value, std::basic_string_view< CharType, Traits > prefix)
Removes a prefix from a string.
Definition: string_helper.h:369
std::tuple< std::basic_string_view< CharType, Traits >, std::optional< std::basic_string_view< CharType, Traits > > > split_once(std::basic_string_view< CharType, Traits > value, CharType separator)
Splits a string on the first occurrence of a separator.
Definition: string_helper.h:389
parsing_mode
Indicates what argument parsing rules should be used to interpret the command line.
Definition: parsing_mode.h:14
@ long_short
Use POSIX-like rules where arguments have separate long and short names.
set_value_result
The result of attempting to set a value for an argument.
Definition: command_line_argument.h:91
@ success
The operation was successful.
@ error
There was an error converting the value to the element type of the argument.
on_parsed_action
Value to be returned from the callback passed to the basic_command_line_parser::on_parsed() method.
Definition: command_line_parser.h:69
@ always_continue
Continue parsing even if command_line_argument::cancel_parsing() returns true.
@ none
Don't take any special action.
@ cancel_parsing
Cancel parsing immediately, disregarding the rest of the command line. Parsing will return with parse...
parse_error
The type of error that occurred while parsing the command line.
Definition: parse_result.h:14
Provides error handling for the ookii::basic_command_line_parser class.
Provides helpers for working with iterator ranges.
A version of the std::less predicate for characters that supports case insensitive comparison.
Definition: string_helper.h:79
Provides the result, success or error, of a command line argument parsing operation.
Definition: parse_result.h:62
A version of the std::less predicate for strings that supports case insensitive comparison.
Definition: string_helper.h:30
Provides the ookii::basic_usage_writer class.