17namespace sharg::detail
46class format_parse :
public format_base
52 format_parse() =
delete;
53 format_parse(format_parse
const & pf) =
default;
54 format_parse & operator=(format_parse
const & pf) =
default;
55 format_parse(format_parse &&) =
default;
56 format_parse & operator=(format_parse &&) =
default;
57 ~format_parse() =
default;
69 template <
typename option_type,
typename val
idator_t>
70 void add_option(option_type & value, config<validator_t>
const & config)
72 option_calls.push_back(
73 [
this, &value, config]()
75 get_option(value, config);
82 template <
typename val
idator_t>
83 void add_flag(
bool & value, config<validator_t>
const & config)
86 [
this, &value, config]()
88 get_flag(value, config.short_id, config.long_id);
95 template <
typename option_type,
typename val
idator_t>
96 void add_positional_option(option_type & value, config<validator_t>
const & config)
98 positional_option_calls.push_back(
99 [
this, &value, config]()
101 get_positional_option(value, config.validator);
106 void parse(parser_meta_data
const & )
108 end_of_options_it =
std::find(arguments.begin(), arguments.end(),
"--");
112 for (
auto && f : option_calls)
115 for (
auto && f : flag_calls)
118 check_for_unknown_ids();
120 if (end_of_options_it != arguments.end())
121 *end_of_options_it =
"";
123 for (
auto && f : positional_option_calls)
126 check_for_left_over_args();
133 void add_subsection(
std::string const &,
bool const)
135 void add_line(
std::string const &,
bool,
bool const)
142 template <
typename id_type>
143 static bool is_empty_id(id_type
const &
id)
172 template <
typename iterator_type,
typename id_type>
173 static iterator_type find_option_id(iterator_type begin_it, iterator_type end_it, id_type
const &
id)
188 return current_arg.
substr(0, full_id.
size()) == full_id;
193 return current_arg.
substr(0, full_id.
size()) == full_id &&
194 (current_arg.
size() == full_id.
size()
195 || current_arg[full_id.
size()] ==
'=');
202 enum class option_parse_result
215 return {
"--" + long_id};
222 static std::string prepend_dash(
char const short_id)
224 return {
'-', short_id};
234 if (short_id ==
'\0')
235 return prepend_dash(long_id);
236 else if (long_id.
empty())
237 return prepend_dash(short_id);
239 return prepend_dash(short_id) +
"/" + prepend_dash(long_id);
247 auto it =
std::find(arguments.begin(), end_of_options_it, prepend_dash(long_id));
249 if (it != end_of_options_it)
252 return (it != end_of_options_it);
258 bool flag_is_set(
char const short_id)
263 if (arg[0] ==
'-' && arg.size() > 1 && arg[1] !=
'-')
265 auto pos = arg.find(short_id);
267 if (pos != std::string::npos)
288 template <
typename option_t>
289 requires istreamable<option_t>
290 option_parse_result parse_option_value(option_t & value,
std::string const & in)
295 if (stream.fail() || !stream.eof())
296 return option_parse_result::error;
298 return option_parse_result::success;
308 template <named_enumeration option_t>
309 option_parse_result parse_option_value(option_t & value,
std::string const & in)
311 auto map = sharg::enumeration_names<option_t>;
313 if (
auto it = map.find(in); it == map.end())
320 key_value_pairs.end(),
321 [](
auto pair1,
auto pair2)
323 if constexpr (std::totally_ordered<option_t>)
325 if (pair1.second != pair2.second)
326 return pair1.second < pair2.second;
329 return pair1.first < pair2.first;
333 for (
auto const & [key, value] : key_value_pairs)
334 result +=
std::string{key.
data()} +
", ";
335 result.replace(result.size() - 2, 2,
"]");
339 throw user_input_error{
"You have chosen an invalid input value: " + in +
". Please use one of: " + keys};
346 return option_parse_result::success;
353 return option_parse_result::success;
369 template <detail::is_container_option container_option_t,
typename format_parse_t = format_parse>
370 requires requires (format_parse_t fp,
371 typename container_option_t::value_type & container_value,
377 option_parse_result parse_option_value(container_option_t & value,
std::string const & in)
379 typename container_option_t::value_type tmp{};
381 auto res = parse_option_value(tmp, in);
383 if (res == option_parse_result::success)
384 value.push_back(tmp);
401 template <
typename option_t>
403 option_parse_result parse_option_value(option_t & value,
std::string const & in)
407 if (res.ec == std::errc::result_out_of_range)
408 return option_parse_result::overflow_error;
409 else if (res.ec == std::errc::invalid_argument || res.ptr != &in[in.
size()])
410 return option_parse_result::error;
412 return option_parse_result::success;
425 option_parse_result parse_option_value(
bool & value,
std::string const & in)
431 else if (in ==
"true")
433 else if (in ==
"false")
436 return option_parse_result::error;
438 return option_parse_result::success;
448 template <
typename option_type>
449 void throw_on_input_error(option_parse_result
const res,
453 std::string msg{
"Value parse failed for " + option_name +
": "};
455 if (res == option_parse_result::error)
457 throw user_input_error{msg +
"Argument " + input_value +
" could not be parsed as type "
458 + get_type_name_as_string(option_type{}) +
"."};
463 if (res == option_parse_result::overflow_error)
465 throw user_input_error{msg +
"Numeric argument " + input_value +
" is not in the valid range ["
471 assert(res == option_parse_result::success);
491 template <
typename option_type,
typename id_type>
492 bool identify_and_retrieve_option_value(option_type & value,
496 if (option_it != end_of_options_it)
499 size_t id_size = (prepend_dash(
id)).
size();
501 if ((*option_it).size() > id_size)
503 if ((*option_it)[id_size] ==
'=')
505 if ((*option_it).size() == id_size + 1)
506 throw too_few_arguments(
"Missing value for option " + prepend_dash(
id));
507 input_value = (*option_it).
substr(id_size + 1);
511 input_value = (*option_it).
substr(id_size);
520 if (option_it == end_of_options_it)
521 throw too_few_arguments(
"Missing value for option " + prepend_dash(
id));
522 input_value = *option_it;
526 auto res = parse_option_value(value, input_value);
527 throw_on_input_error<option_type>(res, prepend_dash(
id), input_value);
551 template <
typename option_type,
typename id_type>
552 bool get_option_by_id(option_type & value, id_type
const &
id)
554 auto it = find_option_id(arguments.begin(), end_of_options_it,
id);
556 if (it != end_of_options_it)
557 identify_and_retrieve_option_value(value, it,
id);
559 if (find_option_id(it, end_of_options_it,
id) != end_of_options_it)
560 throw option_declared_multiple_times(
"Option " + prepend_dash(
id)
561 +
" is no list/container but declared multiple times.");
563 return (it != end_of_options_it);
577 template <detail::is_container_option option_type,
typename id_type>
578 bool get_option_by_id(option_type & value, id_type
const &
id)
580 auto it = find_option_id(arguments.begin(), end_of_options_it,
id);
581 bool seen_at_least_once{it != end_of_options_it};
583 if (seen_at_least_once)
586 while (it != end_of_options_it)
588 identify_and_retrieve_option_value(value, it,
id);
589 it = find_option_id(it, end_of_options_it,
id);
592 return seen_at_least_once;
608 void check_for_unknown_ids()
610 for (
auto it = arguments.begin(); it != end_of_options_it; ++it)
613 if (!arg.empty() && arg[0] ==
'-')
619 else if (arg[1] !=
'-' && arg.size() > 2)
621 throw unknown_option(
"Unknown flags " + expand_multiple_flags(arg)
622 +
". In case this is meant to be a non-option/argument/parameter, "
623 +
"please specify the start of arguments with '--'. "
624 +
"See -h/--help for program information.");
628 throw unknown_option(
"Unknown option " + arg
629 +
". In case this is meant to be a non-option/argument/parameter, "
630 +
"please specify the start of non-options with '--'. "
631 +
"See -h/--help for program information.");
648 void check_for_left_over_args()
657 throw too_many_arguments(
"Too many arguments provided. Please see -h/--help for more information.");
677 template <
typename option_type,
typename val
idator_t>
678 void get_option(option_type & value, config<validator_t>
const & config)
680 bool short_id_is_set{get_option_by_id(value, config.short_id)};
681 bool long_id_is_set{get_option_by_id(value, config.long_id)};
684 if (short_id_is_set && long_id_is_set && !detail::is_container_option<option_type>)
685 throw option_declared_multiple_times(
"Option " + combine_option_names(config.short_id, config.long_id)
686 +
" is no list/container but specified multiple times");
688 if (short_id_is_set || long_id_is_set)
692 config.validator(value);
696 throw validation_error(
std::string(
"Validation failed for option ")
697 + combine_option_names(config.short_id, config.long_id) +
": " + ex.
what());
704 throw required_option_missing(
"Option " + combine_option_names(config.short_id, config.long_id)
705 +
" is required but not set.");
716 void get_flag(
bool & value,
char const short_id,
std::string const & long_id)
720 value = flag_is_set(short_id) || flag_is_set(long_id) || value;
745 template <
typename option_type,
typename val
idator_type>
746 void get_positional_option(option_type & value, validator_type && validator)
748 ++positional_option_count;
756 if (it == arguments.end())
757 throw too_few_arguments(
"Not enough positional arguments provided (Need at least "
759 +
"). See -h/--help for more information.");
761 if constexpr (detail::is_container_option<
764 assert(positional_option_count == positional_option_calls.size());
768 while (it != arguments.end())
770 auto res = parse_option_value(value, *it);
772 throw_on_input_error<option_type>(res,
id, *it);
781 ++positional_option_count;
786 auto res = parse_option_value(value, *it);
788 throw_on_input_error<option_type>(res,
id, *it);
799 throw validation_error(
"Validation failed for positional option " +
std::to_string(positional_option_count)
811 unsigned positional_option_count{0};
The <charconv> header from C++17's standard library.
Provides helper concepts.