25 namespace seqan3::detail
52 class format_parse :
public format_base
58 format_parse() =
delete;
59 format_parse(format_parse
const & pf) =
default;
60 format_parse & operator=(format_parse
const & pf) =
default;
61 format_parse(format_parse &&) =
default;
62 format_parse & operator=(format_parse &&) =
default;
63 ~format_parse() =
default;
69 format_parse(
int const argc_, std::vector<std::string> argv_) :
70 argc{argc_ - 1}, argv{
std::
move(argv_)}
77 template <
typename option_type,
typename val
idator_type>
78 void add_option(option_type & value,
80 std::string
const & long_id,
81 std::string
const & SEQAN3_DOXYGEN_ONLY(desc),
83 validator_type && option_validator)
85 option_calls.push_back([
this, &value, short_id, long_id, spec, option_validator]()
87 get_option(value, short_id, long_id, spec, option_validator);
94 void add_flag(
bool & value,
96 std::string
const & long_id,
97 std::string
const & SEQAN3_DOXYGEN_ONLY(desc),
100 flag_calls.push_back([
this, &value, short_id, long_id]()
102 get_flag(value, short_id, long_id);
109 template <
typename option_type,
typename val
idator_type>
110 void add_positional_option(option_type & value,
111 std::string
const & SEQAN3_DOXYGEN_ONLY(desc),
112 validator_type && option_validator)
114 positional_option_calls.push_back([
this, &value, option_validator]()
116 get_positional_option(value, option_validator);
121 void parse(argument_parser_meta_data
const & )
123 end_of_options_it =
std::find(argv.begin(), argv.end(),
"--");
127 for (
auto && f : option_calls)
130 for (
auto && f : flag_calls)
133 check_for_unknown_ids();
135 if (end_of_options_it != argv.end())
136 *end_of_options_it =
"";
138 for (
auto && f : positional_option_calls)
141 check_for_left_over_args();
146 void add_section(std::string
const &,
option_spec const) {}
147 void add_subsection(std::string
const &,
option_spec const) {}
148 void add_line(std::string
const &,
bool,
option_spec const) {}
149 void add_list_item(std::string
const &, std::string
const &,
option_spec const) {}
153 template <
typename id_type>
154 static bool is_empty_id(id_type
const &
id)
183 template <typename iterator_type, typename id_type>
184 static iterator_type find_option_id(iterator_type begin_it, iterator_type end_it, id_type const &
id)
190 [&] (std::string
const & current_arg)
192 std::string full_id = prepend_dash(
id);
194 if constexpr (std::same_as<id_type, char>)
198 return current_arg.substr(0, full_id.size()) == full_id;
203 return current_arg.substr(0, full_id.size()) == full_id &&
204 (current_arg.size() == full_id.size() || current_arg[full_id.size()] ==
'=');
211 enum class option_parse_result
222 static std::string prepend_dash(std::string
const & long_id)
224 return {
"--" + long_id};
231 static std::string prepend_dash(
char const short_id)
233 return {
"-" + std::string{short_id}};
241 std::string combine_option_names(
char const short_id, std::string
const & long_id)
243 if (short_id ==
'\0')
244 return prepend_dash(long_id);
245 else if (long_id.empty())
246 return prepend_dash(short_id);
248 return prepend_dash(short_id) +
"/" + prepend_dash(long_id);
254 bool flag_is_set(std::string
const & long_id)
256 auto it =
std::find(argv.begin(), end_of_options_it, prepend_dash(long_id));
258 if (it != end_of_options_it)
261 return(it != end_of_options_it);
267 bool flag_is_set(
char const short_id)
270 for (std::string & arg : argv)
272 if (arg[0] ==
'-' && arg.size() > 1 && arg[1] !=
'-')
274 auto pos = arg.find(short_id);
276 if (pos != std::string::npos)
297 template <
typename option_t>
301 option_parse_result parse_option_value(option_t & value, std::string
const & in)
303 std::istringstream stream{in};
306 if (stream.fail() || !stream.eof())
307 return option_parse_result::error;
309 return option_parse_result::success;
319 template <named_enumeration option_t>
320 option_parse_result parse_option_value(option_t & value, std::string
const & in)
322 auto map = seqan3::enumeration_names<option_t>;
324 if (
auto it = map.find(in); it == map.end())
326 std::vector<std::pair<std::string_view, option_t>> key_value_pairs(map.begin(), map.end());
327 std::ranges::sort(key_value_pairs, [] (
auto pair1,
auto pair2)
329 if constexpr (std::totally_ordered<option_t>)
331 if (pair1.second != pair2.second)
332 return pair1.second < pair2.second;
334 return pair1.first < pair2.first;
337 throw user_input_error{detail::to_string(
"You have chosen an invalid input value: ", in,
338 ". Please use one of: ", key_value_pairs | std::views::keys)};
345 return option_parse_result::success;
349 option_parse_result parse_option_value(std::string & value, std::string
const & in)
352 return option_parse_result::success;
364 template <sequence_container container_option_t>
366 requires requires (format_parse fp,
typename container_option_t::value_type & container_value, std::string
const & in)
371 option_parse_result parse_option_value(container_option_t & value, std::string
const & in)
373 typename container_option_t::value_type tmp{};
375 auto res = parse_option_value(tmp, in);
377 if (res == option_parse_result::success)
378 value.push_back(tmp);
395 template <arithmetic option_t>
399 option_parse_result parse_option_value(option_t & value, std::string
const & in)
401 auto res = std::from_chars(&in[0], &in[in.size()], value);
403 if (res.ec == std::errc::result_out_of_range)
404 return option_parse_result::overflow_error;
405 else if (res.ec == std::errc::invalid_argument || res.ptr != &in[in.size()])
406 return option_parse_result::error;
408 return option_parse_result::success;
421 option_parse_result parse_option_value(
bool & value, std::string
const & in)
427 else if (in ==
"true")
429 else if (in ==
"false")
432 return option_parse_result::error;
434 return option_parse_result::success;
444 template <
typename option_type>
445 void throw_on_input_error(option_parse_result
const res,
446 std::string
const & option_name,
447 std::string
const & input_value)
449 std::string msg{
"Value parse failed for " + option_name +
": "};
451 if (res == option_parse_result::error)
453 throw user_input_error{msg +
"Argument " + input_value +
" could not be parsed as type " +
454 get_type_name_as_string(input_value) +
"."};
459 if (res == option_parse_result::overflow_error)
461 throw user_input_error{msg +
"Numeric argument " + input_value +
" is not in the valid range [" +
462 std::to_string(std::numeric_limits<option_type>::min()) +
"," +
463 std::to_string(std::numeric_limits<option_type>::max()) +
"]."};
467 assert(res == option_parse_result::success);
487 template <
typename option_type,
typename id_type>
488 bool identify_and_retrieve_option_value(option_type & value,
489 std::vector<std::string>::iterator & option_it,
492 if (option_it != end_of_options_it)
494 std::string input_value;
495 size_t id_size = (prepend_dash(
id)).
size();
497 if ((*option_it).size() > id_size)
499 if ((*option_it)[id_size] ==
'=')
501 if ((*option_it).size() == id_size + 1)
502 throw too_few_arguments(
"Missing value for option " + prepend_dash(
id));
503 input_value = (*option_it).substr(id_size + 1);
507 input_value = (*option_it).substr(id_size);
516 if (option_it == end_of_options_it)
517 throw too_few_arguments(
"Missing value for option " + prepend_dash(
id));
518 input_value = *option_it;
522 auto res = parse_option_value(value, input_value);
523 throw_on_input_error<option_type>(res, prepend_dash(
id), input_value);
547 template <
typename option_type,
typename id_type>
548 bool get_option_by_id(option_type & value, id_type
const &
id)
550 auto it = find_option_id(argv.begin(), end_of_options_it,
id);
552 if (it != end_of_options_it)
553 identify_and_retrieve_option_value(value, it,
id);
555 if (find_option_id(it, end_of_options_it,
id) != end_of_options_it)
556 throw option_declared_multiple_times(
"Option " + prepend_dash(
id) +
557 " is no list/container but declared multiple times.");
559 return (it != end_of_options_it);
573 template <sequence_container option_type,
typename id_type>
575 requires (!std::is_same_v<option_type, std::string>)
577 bool get_option_by_id(option_type & value, id_type
const &
id)
579 auto it = find_option_id(argv.begin(), end_of_options_it,
id);
580 bool seen_at_least_once{it != end_of_options_it};
582 if (seen_at_least_once)
585 while (it != end_of_options_it)
587 identify_and_retrieve_option_value(value, it,
id);
588 it = find_option_id(it, end_of_options_it,
id);
591 return seen_at_least_once;
607 void check_for_unknown_ids()
609 for (
auto it = argv.begin(); it != end_of_options_it; ++it)
611 std::string arg{*it};
612 if (!arg.empty() && arg[0] ==
'-')
618 else if (arg[1] !=
'-' && arg.size() > 2)
620 throw unknown_option(
"Unknown flags " + expand_multiple_flags(arg) +
621 ". In case this is meant to be a non-option/argument/parameter, " +
622 "please specify the start of arguments with '--'. " +
623 "See -h/--help for program information.");
627 throw unknown_option(
"Unknown option " + arg +
628 ". In case this is meant to be a non-option/argument/parameter, " +
629 "please specify the start of non-options with '--'. " +
630 "See -h/--help for program information.");
647 void check_for_left_over_args()
649 if (
std::find_if(argv.begin(), argv.end(), [](std::string
const & s){return (s !=
"");}) != argv.end())
650 throw too_many_arguments(
"Too many arguments provided. Please see -h/--help for more information.");
673 template <
typename option_type,
typename val
idator_type>
674 void get_option(option_type & value,
676 std::string
const & long_id,
680 bool short_id_is_set{get_option_by_id(value, short_id)};
681 bool long_id_is_set{get_option_by_id(value, long_id)};
684 if (short_id_is_set && long_id_is_set &&
686 throw option_declared_multiple_times(
"Option " + combine_option_names(short_id, long_id) +
687 " is no list/container but specified multiple times");
689 if (short_id_is_set || long_id_is_set)
695 catch (std::exception & ex)
697 throw validation_error(std::string(
"Validation failed for option ") +
698 combine_option_names(short_id, long_id) +
": " + ex.what());
705 throw required_option_missing(
"Option " + combine_option_names(short_id, long_id) +
706 " is required but not set.");
717 void get_flag(
bool & value,
719 std::string
const & long_id)
721 value = flag_is_set(short_id) || flag_is_set(long_id);
746 template <
typename option_type,
typename val
idator_type>
747 void get_positional_option(option_type & value,
750 ++positional_option_count;
751 auto it =
std::find_if(argv.begin(), argv.end(), [](std::string
const & s){return (s !=
"");});
753 if (it == argv.end())
754 throw too_few_arguments(
"Not enough positional arguments provided (Need at least " +
755 std::to_string(positional_option_calls.size()) +
756 "). See -h/--help for more information.");
760 assert(positional_option_count == positional_option_calls.size());
764 while (it != argv.end())
766 auto res = parse_option_value(value, *it);
767 std::string
id =
"positional option" + std::to_string(positional_option_count);
768 throw_on_input_error<option_type>(res,
id, *it);
771 it =
std::find_if(it, argv.end(), [](std::string
const & s){return (s !=
"");});
772 ++positional_option_count;
777 auto res = parse_option_value(value, *it);
778 std::string
id =
"positional option" + std::to_string(positional_option_count);
779 throw_on_input_error<option_type>(res,
id, *it);
788 catch (std::exception & ex)
790 throw validation_error(
"Validation failed for positional option " +
791 std::to_string(positional_option_count) +
": " + ex.what());
796 std::vector<std::function<void()>> option_calls;
798 std::vector<std::function<void()>> flag_calls;
800 std::vector<std::function<void()>> positional_option_calls;
802 unsigned positional_option_count{0};
806 std::vector<std::string> argv;
808 std::vector<std::string>::iterator end_of_options_it;
option_spec
Used to further specify argument_parser options/flags.
Definition: auxiliary.hpp:238
@ required
Definition: auxiliary.hpp:240
constexpr auto is_char
Checks whether a given letter is the same as the template non-type argument.
Definition: predicate.hpp:83
constexpr ptrdiff_t find
Get the index of the first occurrence of a type in a pack.
Definition: traits.hpp:187
constexpr size_t size
The size of a type pack.
Definition: traits.hpp:151
constexpr ptrdiff_t find_if
Get the index of the first type in a pack that satisfies the given predicate.
Definition: traits.hpp:210
auto const move
A view that turns lvalue-references into rvalue-references.
Definition: move.hpp:74
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.
Definition: affine_cell_proxy.hpp:438
typename remove_cvref< t >::type remove_cvref_t
Return the input type with const, volatile and references removed (transformation_trait shortcut).
Definition: type_traits:54
Provides std::from_chars and std::to_chars if not defined in the stdlib <charconv> header.
Provides traits to inspect some information of a type, for example its name.
Provides character predicates for tokenisation.