18namespace sharg::detail
47class format_parse :
public format_base
53 format_parse() =
delete;
54 format_parse(format_parse
const & pf) =
default;
55 format_parse & operator=(format_parse
const & pf) =
default;
56 format_parse(format_parse &&) =
default;
57 format_parse & operator=(format_parse &&) =
default;
58 ~format_parse() =
default;
70 template <
typename option_type,
typename val
idator_t>
71 void add_option(option_type & value, config<validator_t>
const & config)
73 option_calls.push_back(
74 [
this, &value, config]()
76 get_option(value, config);
83 template <
typename val
idator_t>
84 void add_flag(
bool & value, config<validator_t>
const & config)
87 [
this, &value, config]()
89 get_flag(value, config.short_id, config.long_id);
96 template <
typename option_type,
typename val
idator_t>
97 void add_positional_option(option_type & value, config<validator_t>
const & config)
99 positional_option_calls.push_back(
100 [
this, &value, config]()
102 get_positional_option(value, config.validator);
107 void parse(parser_meta_data
const & )
109 end_of_options_it =
std::find(arguments.begin(), arguments.end(),
"--");
113 for (
auto && f : option_calls)
116 for (
auto && f : flag_calls)
119 check_for_unknown_ids();
121 if (end_of_options_it != arguments.end())
122 *end_of_options_it =
"";
124 for (
auto && f : positional_option_calls)
127 check_for_left_over_args();
134 void add_subsection(
std::string const &,
bool const)
136 void add_line(
std::string const &,
bool,
bool const)
163 template <
typename iterator_type>
164 static iterator_type find_option_id(iterator_type begin_it, iterator_type end_it, detail::id_pair
const &
id)
166 bool const short_id_empty{
id.empty_short_id()};
167 bool const long_id_empty{
id.empty_long_id()};
169 if (short_id_empty && long_id_empty)
172 std::string const short_id = prepend_dash(
id.short_id);
173 std::string const long_id_equals = prepend_dash(
id.long_id) +
"=";
177 tmp.remove_suffix(1u);
185 if (!short_id_empty && current_arg.starts_with(short_id))
189 if (!long_id_empty && (current_arg == long_id_space || current_arg.starts_with(long_id_equals)))
200 enum class option_parse_result : uint8_t
213 return {
"--" + long_id};
220 static std::string prepend_dash(
char const short_id)
222 return {
'-', short_id};
232 if (short_id ==
'\0')
233 return prepend_dash(long_id);
234 else if (long_id.
empty())
235 return prepend_dash(short_id);
237 return prepend_dash(short_id) +
"/" + prepend_dash(long_id);
245 auto it =
std::find(arguments.begin(), end_of_options_it, prepend_dash(long_id));
247 if (it != end_of_options_it)
250 return (it != end_of_options_it);
256 bool flag_is_set(
char const short_id)
261 if (arg[0] ==
'-' && arg.size() > 1 && arg[1] !=
'-')
263 auto pos = arg.find(short_id);
265 if (pos != std::string::npos)
286 template <
typename option_t>
287 requires istreamable<option_t>
288 option_parse_result parse_option_value(option_t & value,
std::string const & in)
293 if (stream.fail() || !stream.eof())
294 return option_parse_result::error;
296 return option_parse_result::success;
306 template <named_enumeration option_t>
307 option_parse_result parse_option_value(option_t & value,
std::string const & in)
309 auto map = sharg::enumeration_names<option_t>;
311 if (
auto it = map.find(in); it == map.end())
318 key_value_pairs.end(),
319 [](
auto pair1,
auto pair2)
321 if constexpr (std::totally_ordered<option_t>)
323 if (pair1.second != pair2.second)
324 return pair1.second < pair2.second;
327 return pair1.first < pair2.first;
331 for (
auto const & [key, value] : key_value_pairs)
332 result +=
std::string{key} +
", ";
333 result.replace(result.size() - 2, 2,
"]");
337 throw user_input_error{
"You have chosen an invalid input value: " + in +
". Please use one of: " + keys};
344 return option_parse_result::success;
351 return option_parse_result::success;
367 template <detail::is_container_option container_option_t,
typename format_parse_t = format_parse>
368 requires requires (format_parse_t fp,
369 typename container_option_t::value_type & container_value,
375 option_parse_result parse_option_value(container_option_t & value,
std::string const & in)
377 typename container_option_t::value_type tmp{};
379 auto res = parse_option_value(tmp, in);
381 if (res == option_parse_result::success)
382 value.push_back(tmp);
399 template <
typename option_t>
401 option_parse_result parse_option_value(option_t & value,
std::string const & in)
405 if (res.ec == std::errc::result_out_of_range)
406 return option_parse_result::overflow_error;
407 else if (res.ec == std::errc::invalid_argument || res.ptr != in.
data() + in.
size())
408 return option_parse_result::error;
410 return option_parse_result::success;
423 option_parse_result parse_option_value(
bool & value,
std::string const & in)
425 if (in ==
"0" || in ==
"false")
427 else if (in ==
"1" || in ==
"true")
430 return option_parse_result::error;
432 return option_parse_result::success;
442 template <
typename option_type>
443 void throw_on_input_error(option_parse_result
const res,
447 std::string msg{
"Value parse failed for " + option_name +
": "};
449 if (res == option_parse_result::error)
451 throw user_input_error{msg +
"Argument " + input_value +
" could not be parsed as type "
452 + get_type_name_as_string(option_type{}) +
"."};
457 if (res == option_parse_result::overflow_error)
459 throw user_input_error{msg +
"Numeric argument " + input_value +
" is not in the valid range ["
465 assert(res == option_parse_result::success);
485 template <
typename option_type,
typename id_type>
486 bool identify_and_retrieve_option_value(option_type & value,
490 if (option_it != end_of_options_it)
493 size_t id_size = (prepend_dash(
id)).
size();
495 if ((*option_it).size() > id_size)
497 if ((*option_it)[id_size] ==
'=')
499 if ((*option_it).size() == id_size + 1)
500 throw too_few_arguments(
"Missing value for option " + prepend_dash(
id));
501 input_value = (*option_it).
substr(id_size + 1);
505 input_value = (*option_it).
substr(id_size);
514 if (option_it == end_of_options_it)
515 throw too_few_arguments(
"Missing value for option " + prepend_dash(
id));
516 input_value = *option_it;
520 auto res = parse_option_value(value, input_value);
521 throw_on_input_error<option_type>(res, prepend_dash(
id), input_value);
545 template <
typename option_type,
typename id_type>
546 bool get_option_by_id(option_type & value, id_type
const &
id)
548 auto it = find_option_id(arguments.begin(), end_of_options_it,
id);
550 if (it != end_of_options_it)
551 identify_and_retrieve_option_value(value, it,
id);
553 if (find_option_id(it, end_of_options_it,
id) != end_of_options_it)
554 throw option_declared_multiple_times(
"Option " + prepend_dash(
id)
555 +
" is no list/container but declared multiple times.");
557 return (it != end_of_options_it);
571 template <detail::is_container_option option_type,
typename id_type>
572 bool get_option_by_id(option_type & value, id_type
const &
id)
574 auto it = find_option_id(arguments.begin(), end_of_options_it,
id);
575 bool seen_at_least_once{it != end_of_options_it};
577 if (seen_at_least_once)
580 while (it != end_of_options_it)
582 identify_and_retrieve_option_value(value, it,
id);
583 it = find_option_id(it, end_of_options_it,
id);
586 return seen_at_least_once;
602 void check_for_unknown_ids()
604 for (
auto it = arguments.begin(); it != end_of_options_it; ++it)
607 if (!arg.empty() && arg[0] ==
'-')
613 else if (arg[1] !=
'-' && arg.size() > 2)
615 throw unknown_option(
"Unknown flags " + expand_multiple_flags(arg)
616 +
". In case this is meant to be a non-option/argument/parameter, "
617 +
"please specify the start of arguments with '--'. "
618 +
"See -h/--help for program information.");
622 throw unknown_option(
"Unknown option " + arg
623 +
". In case this is meant to be a non-option/argument/parameter, "
624 +
"please specify the start of non-options with '--'. "
625 +
"See -h/--help for program information.");
642 void check_for_left_over_args()
651 throw too_many_arguments(
"Too many arguments provided. Please see -h/--help for more information.");
671 template <
typename option_type,
typename val
idator_t>
672 void get_option(option_type & value, config<validator_t>
const & config)
674 bool short_id_is_set{get_option_by_id(value, config.short_id)};
675 bool long_id_is_set{get_option_by_id(value, config.long_id)};
678 if (short_id_is_set && long_id_is_set && !detail::is_container_option<option_type>)
679 throw option_declared_multiple_times(
"Option " + combine_option_names(config.short_id, config.long_id)
680 +
" is no list/container but specified multiple times");
682 if (short_id_is_set || long_id_is_set)
686 config.validator(value);
690 throw validation_error(
std::string(
"Validation failed for option ")
691 + combine_option_names(config.short_id, config.long_id) +
": " + ex.
what());
698 throw required_option_missing(
"Option " + combine_option_names(config.short_id, config.long_id)
699 +
" is required but not set.");
710 void get_flag(
bool & value,
char const short_id,
std::string const & long_id)
714 value = flag_is_set(short_id) || flag_is_set(long_id) || value;
739 template <
typename option_type,
typename val
idator_type>
740 void get_positional_option(option_type & value, validator_type && validator)
742 ++positional_option_count;
750 if (it == arguments.end())
751 throw too_few_arguments(
"Not enough positional arguments provided (Need at least "
753 +
"). See -h/--help for more information.");
755 if constexpr (detail::is_container_option<
758 assert(positional_option_count == positional_option_calls.size());
762 while (it != arguments.end())
764 auto res = parse_option_value(value, *it);
766 throw_on_input_error<option_type>(res,
id, *it);
775 ++positional_option_count;
780 auto res = parse_option_value(value, *it);
782 throw_on_input_error<option_type>(res,
id, *it);
793 throw validation_error(
"Validation failed for positional option " +
std::to_string(positional_option_count)
805 unsigned positional_option_count{0};
The <charconv> header from C++17's standard library.
Provides helper concepts.
Provides sharg::detail::id_pair.