20namespace sharg::detail
49class format_parse :
public format_base
55 format_parse() =
delete;
56 format_parse(format_parse
const & pf) =
default;
57 format_parse & operator=(format_parse
const & pf) =
default;
58 format_parse(format_parse &&) =
default;
59 format_parse & operator=(format_parse &&) =
default;
60 ~format_parse() =
default;
75 template <
typename option_type,
typename val
idator_t>
76 void add_option(option_type & value, config<validator_t>
const & config)
78 option_calls.push_back(
79 [
this, &value, config]()
81 get_option(value, config);
88 template <
typename val
idator_t>
89 void add_flag(
bool & value, config<validator_t>
const & config)
92 [
this, &value, config]()
94 get_flag(value, config.short_id, config.long_id);
101 template <
typename option_type,
typename val
idator_t>
102 void add_positional_option(option_type & value, config<validator_t>
const & config)
104 positional_option_calls.push_back(
105 [
this, &value, config]()
107 get_positional_option(value, config.validator);
112 void parse(parser_meta_data
const & )
114 end_of_options_it =
std::find(argv.begin(), argv.end(),
"--");
118 for (
auto && f : option_calls)
121 for (
auto && f : flag_calls)
124 check_for_unknown_ids();
126 if (end_of_options_it != argv.end())
127 *end_of_options_it =
"";
129 for (
auto && f : positional_option_calls)
132 check_for_left_over_args();
139 void add_subsection(
std::string const &,
bool const)
141 void add_line(
std::string const &,
bool,
bool const)
148 template <
typename id_type>
149 static bool is_empty_id(id_type
const &
id)
151 if constexpr (std::same_as<std::remove_cvref_t<id_type>,
std::string>)
178 template <
typename iterator_type,
typename id_type>
179 static iterator_type find_option_id(iterator_type begin_it, iterator_type end_it, id_type
const &
id)
184 return (std::find_if(begin_it,
190 if constexpr (std::same_as<id_type, char>)
194 return current_arg.
substr(0, full_id.
size()) == full_id;
199 return current_arg.
substr(0, full_id.
size()) == full_id &&
200 (current_arg.
size() == full_id.
size()
201 || current_arg[full_id.
size()] ==
'=');
208 enum class option_parse_result
221 return {
"--" + long_id};
228 static std::string prepend_dash(
char const short_id)
230 return {
'-', short_id};
240 if (short_id ==
'\0')
241 return prepend_dash(long_id);
242 else if (long_id.
empty())
243 return prepend_dash(short_id);
245 return prepend_dash(short_id) +
"/" + prepend_dash(long_id);
253 auto it =
std::find(argv.begin(), end_of_options_it, prepend_dash(long_id));
255 if (it != end_of_options_it)
258 return (it != end_of_options_it);
264 bool flag_is_set(
char const short_id)
269 if (arg[0] ==
'-' && arg.size() > 1 && arg[1] !=
'-')
271 auto pos = arg.find(short_id);
273 if (pos != std::string::npos)
294 template <
typename option_t>
295 requires istreamable<option_t>
296 option_parse_result parse_option_value(option_t & value,
std::string const & in)
301 if (stream.fail() || !stream.eof())
302 return option_parse_result::error;
304 return option_parse_result::success;
314 template <named_enumeration option_t>
315 option_parse_result parse_option_value(option_t & value,
std::string const & in)
317 auto map = sharg::enumeration_names<option_t>;
319 if (
auto it = map.find(in); it == map.end())
326 key_value_pairs.end(),
327 [](
auto pair1,
auto pair2)
329 if constexpr (std::totally_ordered<option_t>)
331 if (pair1.second != pair2.second)
332 return pair1.second < pair2.second;
335 return pair1.first < pair2.first;
339 for (
auto const & [key, value] : key_value_pairs)
341 result.replace(result.size() - 2, 2,
"]");
345 throw user_input_error{
"You have chosen an invalid input value: " + in +
". Please use one of: " + keys};
352 return option_parse_result::success;
359 return option_parse_result::success;
375 template <detail::is_container_option container_option_t,
typename format_parse_t = format_parse>
376 requires requires (format_parse_t fp,
377 typename container_option_t::value_type & container_value,
380 {fp.parse_option_value(container_value, in)} -> std::same_as<option_parse_result>;
383 option_parse_result parse_option_value(container_option_t & value,
std::string const & in)
385 typename container_option_t::value_type tmp{};
387 auto res = parse_option_value(tmp, in);
389 if (res == option_parse_result::success)
390 value.push_back(tmp);
407 template <
typename option_t>
408 requires std::is_arithmetic_v<option_t> && istreamable<option_t>
409 option_parse_result parse_option_value(option_t & value,
std::string const & in)
413 if (res.ec == std::errc::result_out_of_range)
414 return option_parse_result::overflow_error;
415 else if (res.ec == std::errc::invalid_argument || res.ptr != &in[in.
size()])
416 return option_parse_result::error;
418 return option_parse_result::success;
431 option_parse_result parse_option_value(
bool & value,
std::string const & in)
437 else if (in ==
"true")
439 else if (in ==
"false")
442 return option_parse_result::error;
444 return option_parse_result::success;
454 template <
typename option_type>
455 void throw_on_input_error(option_parse_result
const res,
459 std::string msg{
"Value parse failed for " + option_name +
": "};
461 if (res == option_parse_result::error)
463 throw user_input_error{msg +
"Argument " + input_value +
" could not be parsed as type "
464 + get_type_name_as_string(option_type{}) +
"."};
467 if constexpr (std::is_arithmetic_v<option_type>)
469 if (res == option_parse_result::overflow_error)
471 throw user_input_error{msg +
"Numeric argument " + input_value +
" is not in the valid range ["
477 assert(res == option_parse_result::success);
497 template <
typename option_type,
typename id_type>
498 bool identify_and_retrieve_option_value(option_type & value,
502 if (option_it != end_of_options_it)
505 size_t id_size = (prepend_dash(
id)).
size();
507 if ((*option_it).size() > id_size)
509 if ((*option_it)[id_size] ==
'=')
511 if ((*option_it).size() == id_size + 1)
512 throw too_few_arguments(
"Missing value for option " + prepend_dash(
id));
513 input_value = (*option_it).
substr(id_size + 1);
517 input_value = (*option_it).
substr(id_size);
526 if (option_it == end_of_options_it)
527 throw too_few_arguments(
"Missing value for option " + prepend_dash(
id));
528 input_value = *option_it;
532 auto res = parse_option_value(value, input_value);
533 throw_on_input_error<option_type>(res, prepend_dash(
id), input_value);
557 template <
typename option_type,
typename id_type>
558 bool get_option_by_id(option_type & value, id_type
const &
id)
560 auto it = find_option_id(argv.begin(), end_of_options_it,
id);
562 if (it != end_of_options_it)
563 identify_and_retrieve_option_value(value, it,
id);
565 if (find_option_id(it, end_of_options_it,
id) != end_of_options_it)
566 throw option_declared_multiple_times(
"Option " + prepend_dash(
id)
567 +
" is no list/container but declared multiple times.");
569 return (it != end_of_options_it);
583 template <detail::is_container_option option_type,
typename id_type>
584 bool get_option_by_id(option_type & value, id_type
const &
id)
586 auto it = find_option_id(argv.begin(), end_of_options_it,
id);
587 bool seen_at_least_once{it != end_of_options_it};
589 if (seen_at_least_once)
592 while (it != end_of_options_it)
594 identify_and_retrieve_option_value(value, it,
id);
595 it = find_option_id(it, end_of_options_it,
id);
598 return seen_at_least_once;
614 void check_for_unknown_ids()
616 for (
auto it = argv.begin(); it != end_of_options_it; ++it)
619 if (!arg.empty() && arg[0] ==
'-')
625 else if (arg[1] !=
'-' && arg.size() > 2)
627 throw unknown_option(
"Unknown flags " + expand_multiple_flags(arg)
628 +
". In case this is meant to be a non-option/argument/parameter, "
629 +
"please specify the start of arguments with '--'. "
630 +
"See -h/--help for program information.");
634 throw unknown_option(
"Unknown option " + arg
635 +
". In case this is meant to be a non-option/argument/parameter, "
636 +
"please specify the start of non-options with '--'. "
637 +
"See -h/--help for program information.");
654 void check_for_left_over_args()
656 if (std::find_if(argv.begin(),
663 throw too_many_arguments(
"Too many arguments provided. Please see -h/--help for more information.");
683 template <
typename option_type,
typename val
idator_t>
684 void get_option(option_type & value, config<validator_t>
const & config)
686 bool short_id_is_set{get_option_by_id(value, config.short_id)};
687 bool long_id_is_set{get_option_by_id(value, config.long_id)};
690 if (short_id_is_set && long_id_is_set && !detail::is_container_option<option_type>)
691 throw option_declared_multiple_times(
"Option " + combine_option_names(config.short_id, config.long_id)
692 +
" is no list/container but specified multiple times");
694 if (short_id_is_set || long_id_is_set)
698 config.validator(value);
702 throw validation_error(
std::string(
"Validation failed for option ")
703 + combine_option_names(config.short_id, config.long_id) +
": " + ex.
what());
710 throw required_option_missing(
"Option " + combine_option_names(config.short_id, config.long_id)
711 +
" is required but not set.");
722 void get_flag(
bool & value,
char const short_id,
std::string const & long_id)
724 value = flag_is_set(short_id) || flag_is_set(long_id);
749 template <
typename option_type,
typename val
idator_type>
750 void get_positional_option(option_type & value, validator_type && validator)
752 ++positional_option_count;
760 if (it == argv.end())
761 throw too_few_arguments(
"Not enough positional arguments provided (Need at least "
762 + std::to_string(positional_option_calls.size())
763 +
"). See -h/--help for more information.");
765 if constexpr (detail::is_container_option<
768 assert(positional_option_count == positional_option_calls.size());
772 while (it != argv.end())
774 auto res = parse_option_value(value, *it);
776 throw_on_input_error<option_type>(res,
id, *it);
785 ++positional_option_count;
790 auto res = parse_option_value(value, *it);
792 throw_on_input_error<option_type>(res,
id, *it);
803 throw validation_error(
"Validation failed for positional option " + std::to_string(positional_option_count)
815 unsigned positional_option_count{0};
The <charconv> header from C++17's standard library.
Provides helper concepts.