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;
73 template <
typename option_type,
typename config_type>
74 void add_option(option_type & value, config_type
const & config)
76 option_calls.push_back(
77 [
this, &value, config]()
79 get_option(value, config);
86 template <
typename config_type>
87 void add_flag(
bool & value, config_type
const & config)
90 [
this, &value, config]()
92 get_flag(value, config.short_id, config.long_id);
99 template <
typename option_type,
typename config_type>
100 void add_positional_option(option_type & value, config_type
const & config)
102 positional_option_calls.push_back(
103 [
this, &value, config]()
105 get_positional_option(value, config.validator);
110 void parse(parser_meta_data
const & )
112 end_of_options_it =
std::find(argv.begin(), argv.end(),
"--");
116 for (
auto && f : option_calls)
119 for (
auto && f : flag_calls)
122 check_for_unknown_ids();
124 if (end_of_options_it != argv.end())
125 *end_of_options_it =
"";
127 for (
auto && f : positional_option_calls)
130 check_for_left_over_args();
137 void add_subsection(
std::string const &,
bool const)
139 void add_line(
std::string const &,
bool,
bool const)
146 template <
typename id_type>
147 static bool is_empty_id(id_type
const &
id)
149 if constexpr (std::same_as<std::remove_cvref_t<id_type>,
std::string>)
176 template <
typename iterator_type,
typename id_type>
177 static iterator_type find_option_id(iterator_type begin_it, iterator_type end_it, id_type
const &
id)
182 return (std::find_if(begin_it,
188 if constexpr (std::same_as<id_type, char>)
192 return current_arg.
substr(0, full_id.
size()) == full_id;
197 return current_arg.
substr(0, full_id.
size()) == full_id &&
198 (current_arg.
size() == full_id.
size()
199 || current_arg[full_id.
size()] ==
'=');
206 enum class option_parse_result
219 return {
"--" + long_id};
226 static std::string prepend_dash(
char const short_id)
228 return {
'-', short_id};
238 if (short_id ==
'\0')
239 return prepend_dash(long_id);
240 else if (long_id.
empty())
241 return prepend_dash(short_id);
243 return prepend_dash(short_id) +
"/" + prepend_dash(long_id);
251 auto it =
std::find(argv.begin(), end_of_options_it, prepend_dash(long_id));
253 if (it != end_of_options_it)
256 return (it != end_of_options_it);
262 bool flag_is_set(
char const short_id)
267 if (arg[0] ==
'-' && arg.size() > 1 && arg[1] !=
'-')
269 auto pos = arg.find(short_id);
271 if (pos != std::string::npos)
292 template <
typename option_t>
293 requires istreamable<option_t>
294 option_parse_result parse_option_value(option_t & value,
std::string const & in)
299 if (stream.fail() || !stream.eof())
300 return option_parse_result::error;
302 return option_parse_result::success;
312 template <named_enumeration option_t>
313 option_parse_result parse_option_value(option_t & value,
std::string const & in)
315 auto map = sharg::enumeration_names<option_t>;
317 if (
auto it = map.find(in); it == map.end())
324 key_value_pairs.end(),
325 [](
auto pair1,
auto pair2)
327 if constexpr (std::totally_ordered<option_t>)
329 if (pair1.second != pair2.second)
330 return pair1.second < pair2.second;
333 return pair1.first < pair2.first;
337 for (
auto const & [key, value] : key_value_pairs)
339 result.replace(result.size() - 2, 2,
"]");
343 throw user_input_error{
"You have chosen an invalid input value: " + in +
". Please use one of: " + keys};
350 return option_parse_result::success;
357 return option_parse_result::success;
373 template <detail::is_container_option container_option_t,
typename format_parse_t = format_parse>
374 requires requires (format_parse_t fp,
375 typename container_option_t::value_type & container_value,
378 {fp.parse_option_value(container_value, in)} -> std::same_as<option_parse_result>;
381 option_parse_result parse_option_value(container_option_t & value,
std::string const & in)
383 typename container_option_t::value_type tmp{};
385 auto res = parse_option_value(tmp, in);
387 if (res == option_parse_result::success)
388 value.push_back(tmp);
405 template <
typename option_t>
406 requires std::is_arithmetic_v<option_t> && istreamable<option_t>
407 option_parse_result parse_option_value(option_t & value,
std::string const & in)
411 if (res.ec == std::errc::result_out_of_range)
412 return option_parse_result::overflow_error;
413 else if (res.ec == std::errc::invalid_argument || res.ptr != &in[in.
size()])
414 return option_parse_result::error;
416 return option_parse_result::success;
429 option_parse_result parse_option_value(
bool & value,
std::string const & in)
435 else if (in ==
"true")
437 else if (in ==
"false")
440 return option_parse_result::error;
442 return option_parse_result::success;
452 template <
typename option_type>
453 void throw_on_input_error(option_parse_result
const res,
457 std::string msg{
"Value parse failed for " + option_name +
": "};
459 if (res == option_parse_result::error)
461 throw user_input_error{msg +
"Argument " + input_value +
" could not be parsed as type "
462 + get_type_name_as_string(option_type{}) +
"."};
465 if constexpr (std::is_arithmetic_v<option_type>)
467 if (res == option_parse_result::overflow_error)
469 throw user_input_error{msg +
"Numeric argument " + input_value +
" is not in the valid range ["
475 assert(res == option_parse_result::success);
495 template <
typename option_type,
typename id_type>
496 bool identify_and_retrieve_option_value(option_type & value,
500 if (option_it != end_of_options_it)
503 size_t id_size = (prepend_dash(
id)).
size();
505 if ((*option_it).size() > id_size)
507 if ((*option_it)[id_size] ==
'=')
509 if ((*option_it).size() == id_size + 1)
510 throw too_few_arguments(
"Missing value for option " + prepend_dash(
id));
511 input_value = (*option_it).
substr(id_size + 1);
515 input_value = (*option_it).
substr(id_size);
524 if (option_it == end_of_options_it)
525 throw too_few_arguments(
"Missing value for option " + prepend_dash(
id));
526 input_value = *option_it;
530 auto res = parse_option_value(value, input_value);
531 throw_on_input_error<option_type>(res, prepend_dash(
id), input_value);
555 template <
typename option_type,
typename id_type>
556 bool get_option_by_id(option_type & value, id_type
const &
id)
558 auto it = find_option_id(argv.begin(), end_of_options_it,
id);
560 if (it != end_of_options_it)
561 identify_and_retrieve_option_value(value, it,
id);
563 if (find_option_id(it, end_of_options_it,
id) != end_of_options_it)
564 throw option_declared_multiple_times(
"Option " + prepend_dash(
id)
565 +
" is no list/container but declared multiple times.");
567 return (it != end_of_options_it);
581 template <detail::is_container_option option_type,
typename id_type>
582 bool get_option_by_id(option_type & value, id_type
const &
id)
584 auto it = find_option_id(argv.begin(), end_of_options_it,
id);
585 bool seen_at_least_once{it != end_of_options_it};
587 if (seen_at_least_once)
590 while (it != end_of_options_it)
592 identify_and_retrieve_option_value(value, it,
id);
593 it = find_option_id(it, end_of_options_it,
id);
596 return seen_at_least_once;
612 void check_for_unknown_ids()
614 for (
auto it = argv.begin(); it != end_of_options_it; ++it)
617 if (!arg.empty() && arg[0] ==
'-')
623 else if (arg[1] !=
'-' && arg.size() > 2)
625 throw unknown_option(
"Unknown flags " + expand_multiple_flags(arg)
626 +
". In case this is meant to be a non-option/argument/parameter, "
627 +
"please specify the start of arguments with '--'. "
628 +
"See -h/--help for program information.");
632 throw unknown_option(
"Unknown option " + arg
633 +
". In case this is meant to be a non-option/argument/parameter, "
634 +
"please specify the start of non-options with '--'. "
635 +
"See -h/--help for program information.");
652 void check_for_left_over_args()
654 if (std::find_if(argv.begin(),
661 throw too_many_arguments(
"Too many arguments provided. Please see -h/--help for more information.");
681 template <
typename option_type,
typename config_type>
682 void get_option(option_type & value, config_type
const & config)
684 bool short_id_is_set{get_option_by_id(value, config.short_id)};
685 bool long_id_is_set{get_option_by_id(value, config.long_id)};
688 if (short_id_is_set && long_id_is_set && !detail::is_container_option<option_type>)
689 throw option_declared_multiple_times(
"Option " + combine_option_names(config.short_id, config.long_id)
690 +
" is no list/container but specified multiple times");
692 if (short_id_is_set || long_id_is_set)
696 config.validator(value);
700 throw validation_error(
std::string(
"Validation failed for option ")
701 + combine_option_names(config.short_id, config.long_id) +
": " + ex.
what());
708 throw required_option_missing(
"Option " + combine_option_names(config.short_id, config.long_id)
709 +
" is required but not set.");
720 void get_flag(
bool & value,
char const short_id,
std::string const & long_id)
722 value = flag_is_set(short_id) || flag_is_set(long_id);
747 template <
typename option_type,
typename val
idator_type>
748 void get_positional_option(option_type & value, validator_type && validator)
750 ++positional_option_count;
758 if (it == argv.end())
759 throw too_few_arguments(
"Not enough positional arguments provided (Need at least "
760 + std::to_string(positional_option_calls.size())
761 +
"). See -h/--help for more information.");
763 if constexpr (detail::is_container_option<
766 assert(positional_option_count == positional_option_calls.size());
770 while (it != argv.end())
772 auto res = parse_option_value(value, *it);
774 throw_on_input_error<option_type>(res,
id, *it);
783 ++positional_option_count;
788 auto res = parse_option_value(value, *it);
790 throw_on_input_error<option_type>(res,
id, *it);
801 throw validation_error(
"Validation failed for positional option " + std::to_string(positional_option_count)
813 unsigned positional_option_count{0};
The <charconv> header from C++17's standard library.
Provides helper concepts.
from_chars_result from_chars(char const *first, char const *last, floating_point_type &value, chars_format fmt=chars_format::general) noexcept
Parse a char sequence into an floating point value.
Definition: charconv:260