25namespace seqan3::detail
54class format_parse :
public format_base
60 format_parse() =
delete;
61 format_parse(format_parse
const & pf) =
default;
62 format_parse & operator=(format_parse
const & pf) =
default;
63 format_parse(format_parse &&) =
default;
64 format_parse & operator=(format_parse &&) =
default;
65 ~format_parse() =
default;
72 argc{argc_ - 1}, argv{
std::move(argv_)}
79 template <
typename option_type,
typename val
idator_type>
80 void add_option(option_type & value,
85 validator_type && option_validator)
87 option_calls.push_back([
this, &value, short_id, long_id, spec, option_validator]()
89 get_option(value, short_id, long_id, spec, option_validator);
96 void add_flag(
bool & value,
102 flag_calls.push_back([
this, &value, short_id, long_id]()
104 get_flag(value, short_id, long_id);
111 template <
typename option_type,
typename val
idator_type>
112 void add_positional_option(option_type & value,
114 validator_type && option_validator)
116 positional_option_calls.push_back([
this, &value, option_validator]()
118 get_positional_option(value, option_validator);
123 void parse(argument_parser_meta_data
const & )
125 end_of_options_it =
std::find(argv.begin(), argv.end(),
"--");
129 for (
auto && f : option_calls)
132 for (
auto && f : flag_calls)
135 check_for_unknown_ids();
137 if (end_of_options_it != argv.end())
138 *end_of_options_it =
"";
140 for (
auto && f : positional_option_calls)
143 check_for_left_over_args();
155 template <
typename id_type>
156 static bool is_empty_id(id_type
const &
id)
158 if constexpr (std::same_as<std::remove_cvref_t<id_type>,
std::string>)
161 return is_char<'\0'>(
id);
185 template <
typename iterator_type,
typename id_type>
186 static iterator_type find_option_id(iterator_type begin_it, iterator_type end_it, id_type
const &
id)
196 if constexpr (std::same_as<id_type, char>)
200 return current_arg.
substr(0, full_id.
size()) == full_id;
205 return current_arg.
substr(0, full_id.
size()) == full_id &&
206 (current_arg.
size() == full_id.
size() || current_arg[full_id.
size()] ==
'=');
213 enum class option_parse_result
226 return {
"--" + long_id};
233 static std::string prepend_dash(
char const short_id)
245 if (short_id ==
'\0')
246 return prepend_dash(long_id);
247 else if (long_id.
empty())
248 return prepend_dash(short_id);
250 return prepend_dash(short_id) +
"/" + prepend_dash(long_id);
258 auto it =
std::find(argv.begin(), end_of_options_it, prepend_dash(long_id));
260 if (it != end_of_options_it)
263 return(it != end_of_options_it);
269 bool flag_is_set(
char const short_id)
274 if (arg[0] ==
'-' && arg.size() > 1 && arg[1] !=
'-')
276 auto pos = arg.find(short_id);
278 if (pos != std::string::npos)
299 template <
typename option_t>
303 option_parse_result parse_option_value(option_t & value,
std::string const & in)
308 if (stream.fail() || !stream.eof())
309 return option_parse_result::error;
311 return option_parse_result::success;
321 template <named_enumeration option_t>
322 option_parse_result parse_option_value(option_t & value,
std::string const & in)
324 auto map = seqan3::enumeration_names<option_t>;
326 if (
auto it = map.find(in); it == map.end())
329 std::ranges::sort(key_value_pairs, [] (
auto pair1,
auto pair2)
331 if constexpr (std::totally_ordered<option_t>)
333 if (pair1.second != pair2.second)
334 return pair1.second < pair2.second;
336 return pair1.first < pair2.first;
339 throw user_input_error{detail::to_string(
"You have chosen an invalid input value: ", in,
340 ". Please use one of: ", key_value_pairs | std::views::keys)};
347 return option_parse_result::success;
354 return option_parse_result::success;
366 template <sequence_container container_option_t>
368 requires requires (format_parse fp,
typename container_option_t::value_type & container_value,
std::string const & in)
373 option_parse_result parse_option_value(container_option_t & value,
std::string const & in)
375 typename container_option_t::value_type tmp{};
377 auto res = parse_option_value(tmp, in);
379 if (res == option_parse_result::success)
380 value.push_back(tmp);
397 template <arithmetic 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[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)
429 else if (in ==
"true")
431 else if (in ==
"false")
434 return option_parse_result::error;
436 return option_parse_result::success;
446 template <
typename option_type>
447 void throw_on_input_error(option_parse_result
const res,
451 std::string msg{
"Value parse failed for " + option_name +
": "};
453 if (res == option_parse_result::error)
455 throw user_input_error{msg +
"Argument " + input_value +
" could not be parsed as type " +
456 get_type_name_as_string(option_type{}) +
"."};
461 if (res == option_parse_result::overflow_error)
463 throw user_input_error{msg +
"Numeric argument " + input_value +
" is not in the valid range [" +
469 assert(res == option_parse_result::success);
489 template <
typename option_type,
typename id_type>
490 bool identify_and_retrieve_option_value(option_type & value,
494 if (option_it != end_of_options_it)
497 size_t id_size = (prepend_dash(
id)).
size();
499 if ((*option_it).size() > id_size)
501 if ((*option_it)[id_size] ==
'=')
503 if ((*option_it).size() == id_size + 1)
504 throw too_few_arguments(
"Missing value for option " + prepend_dash(
id));
505 input_value = (*option_it).
substr(id_size + 1);
509 input_value = (*option_it).
substr(id_size);
518 if (option_it == end_of_options_it)
519 throw too_few_arguments(
"Missing value for option " + prepend_dash(
id));
520 input_value = *option_it;
524 auto res = parse_option_value(value, input_value);
525 throw_on_input_error<option_type>(res, prepend_dash(
id), input_value);
549 template <
typename option_type,
typename id_type>
550 bool get_option_by_id(option_type & value, id_type
const &
id)
552 auto it = find_option_id(argv.begin(), end_of_options_it,
id);
554 if (it != end_of_options_it)
555 identify_and_retrieve_option_value(value, it,
id);
557 if (find_option_id(it, end_of_options_it,
id) != end_of_options_it)
558 throw option_declared_multiple_times(
"Option " + prepend_dash(
id) +
559 " is no list/container but declared multiple times.");
561 return (it != end_of_options_it);
575 template <sequence_container option_type,
typename id_type>
577 requires (!std::is_same_v<option_type, std::string>)
579 bool get_option_by_id(option_type & value, id_type
const &
id)
581 auto it = find_option_id(argv.begin(), end_of_options_it,
id);
582 bool seen_at_least_once{it != end_of_options_it};
584 if (seen_at_least_once)
587 while (it != end_of_options_it)
589 identify_and_retrieve_option_value(value, it,
id);
590 it = find_option_id(it, end_of_options_it,
id);
593 return seen_at_least_once;
609 void check_for_unknown_ids()
611 for (
auto it = argv.begin(); it != end_of_options_it; ++it)
614 if (!arg.empty() && arg[0] ==
'-')
620 else if (arg[1] !=
'-' && arg.size() > 2)
622 throw unknown_option(
"Unknown flags " + expand_multiple_flags(arg) +
623 ". In case this is meant to be a non-option/argument/parameter, " +
624 "please specify the start of arguments with '--'. " +
625 "See -h/--help for program information.");
629 throw unknown_option(
"Unknown option " + arg +
630 ". In case this is meant to be a non-option/argument/parameter, " +
631 "please specify the start of non-options with '--'. " +
632 "See -h/--help for program information.");
649 void check_for_left_over_args()
652 throw too_many_arguments(
"Too many arguments provided. Please see -h/--help for more information.");
675 template <
typename option_type,
typename val
idator_type>
676 void get_option(option_type & value,
682 bool short_id_is_set{get_option_by_id(value, short_id)};
683 bool long_id_is_set{get_option_by_id(value, long_id)};
686 if (short_id_is_set && long_id_is_set &&
688 throw option_declared_multiple_times(
"Option " + combine_option_names(short_id, long_id) +
689 " is no list/container but specified multiple times");
691 if (short_id_is_set || long_id_is_set)
699 throw validation_error(
std::string(
"Validation failed for option ") +
700 combine_option_names(short_id, long_id) +
": " + ex.
what());
707 throw required_option_missing(
"Option " + combine_option_names(short_id, long_id) +
708 " is required but not set.");
719 void get_flag(
bool & value,
723 value = flag_is_set(short_id) || flag_is_set(long_id);
748 template <
typename option_type,
typename val
idator_type>
749 void get_positional_option(option_type & value,
752 ++positional_option_count;
755 if (it == argv.end())
756 throw too_few_arguments(
"Not enough positional arguments provided (Need at least " +
758 "). See -h/--help for more information.");
762 assert(positional_option_count == positional_option_calls.size());
766 while (it != argv.end())
768 auto res = parse_option_value(value, *it);
770 throw_on_input_error<option_type>(res,
id, *it);
774 ++positional_option_count;
779 auto res = parse_option_value(value, *it);
781 throw_on_input_error<option_type>(res,
id, *it);
792 throw validation_error(
"Validation failed for positional option " +
804 unsigned positional_option_count{0};
The <concepts> header from C++20's standard library.
option_spec
Used to further specify argument_parser options/flags.
Definition: auxiliary.hpp:249
@ required
Definition: auxiliary.hpp:251
constexpr size_t size
The size of a type pack.
Definition: traits.hpp:151
A type that satisfies std::is_arithmetic_v<t>.
A more refined container concept than seqan3::container.
The concept for option validators passed to add_option/positional_option.
SeqAn specific customisations in the standard namespace.
Provides character predicates for tokenisation.
The <charconv> header from C++17's standard library.
Provides traits to inspect some information of a type, for example its name.