@@ -497,6 +497,12 @@ class argument_parser {
497
497
arg_name, " An argument name cannot be empty."
498
498
);
499
499
500
+ // TODO: add tests
501
+ if (detail::contains_whitespaces (arg_name))
502
+ throw invalid_configuration::invalid_argument_name (
503
+ arg_name, " An argument name cannot contain whitespaces."
504
+ );
505
+
500
506
if (arg_name.front () == this ->_flag_prefix_char )
501
507
throw invalid_configuration::invalid_argument_name (
502
508
arg_name,
@@ -570,7 +576,7 @@ class argument_parser {
570
576
* 1. No required positional argument can be added after a non-required positional argument.
571
577
*/
572
578
void _validate_argument_configuration () const {
573
- // Step: 1
579
+ // step 1
574
580
const_arg_opt_t non_required_arg = std::nullopt;
575
581
for (const auto & arg : this ->_positional_args ) {
576
582
if (not arg->is_required ()) {
@@ -591,47 +597,74 @@ class argument_parser {
591
597
* @param arg_range The command-line argument value range.
592
598
* @return A list of preprocessed command-line argument tokens.
593
599
*/
594
- template <detail::c_sized_range_of<std::string , detail::type_validator::convertible> AR>
600
+ template <detail::c_sized_range_of<std::string_view , detail::type_validator::convertible> AR>
595
601
[[nodiscard]] arg_token_list_t _tokenize (const AR& arg_range) const noexcept {
596
602
const auto n_args = std::ranges::size (arg_range);
597
603
if (n_args == 0ull )
598
604
return arg_token_list_t {};
599
605
600
606
arg_token_list_t toks;
601
607
toks.reserve (n_args);
608
+ std::ranges::for_each (
609
+ arg_range, std::bind_front (&argument_parser::_tokenize_arg, this , std::ref (toks))
610
+ );
611
+ return toks;
612
+ }
602
613
603
- for (const auto & arg : arg_range) {
604
- std::string value = static_cast <std::string>(arg);
605
- if (this ->_is_flag (value)) {
606
- const auto flag_tok = this ->_strip_flag_prefix (value);
607
- toks.emplace_back (flag_tok, std::move (value));
608
- }
609
- else {
610
- toks.emplace_back (detail::argument_token::t_value, std::move (value));
611
- }
614
+ /* *
615
+ * @brief Appends an argument token created from `arg_value` to the `toks` vector.
616
+ * @param toks The argument token list to which the processed token(s) will be appended.
617
+ * @param arg_value The command-line argument's value to be processed.
618
+ */
619
+ void _tokenize_arg (arg_token_list_t & toks, const std::string_view arg_value) const {
620
+ auto tok = this ->_build_token (arg_value);
621
+ if (tok.is_flag_token () and not this ->_is_valid_flag (tok)) {
622
+ #ifdef AP_UNKNOWN_FLAGS_AS_VALUES
623
+ toks.emplace_back (detail::argument_token::t_value, std::string (arg_value));
624
+ return ;
625
+ #else
626
+ throw parsing_failure::unknown_argument (arg_value);
627
+ #endif
612
628
}
613
629
614
- return toks;
630
+ toks. emplace_back ( std::move (tok)) ;
615
631
}
616
632
617
633
/* *
618
- * @brief Check if an argument is a flag based on its value.
619
- * @param arg The cmd argument's value.
620
- * @return True if the argument is a flag, false otherwise .
634
+ * @brief Builds an argument token from the given value.
635
+ * @param arg_value The command-line argument's value to be processed .
636
+ * @return An argument token with removed flag prefix (if present) and an adequate token type .
621
637
*/
622
- [[nodiscard]] bool _is_flag (const std::string& arg) const noexcept {
623
- if (arg.starts_with (this ->_flag_prefix ))
624
- return this ->_is_arg_name_used (
625
- {arg.substr (this ->_primary_flag_prefix_length )}, detail::argument_name::m_primary
626
- );
638
+ [[nodiscard]] detail::argument_token _build_token (const std::string_view arg_value
639
+ ) const noexcept {
640
+ if (detail::contains_whitespaces (arg_value))
641
+ return {.type = detail::argument_token::t_value, .value = std::string (arg_value)};
627
642
628
- if (arg .starts_with (this ->_flag_prefix_char ))
629
- return this -> _is_arg_name_used (
630
- {arg. substr ( this -> _secondary_flag_prefix_length )} ,
631
- detail::argument_name::m_secondary
632
- ) ;
643
+ if (arg_value .starts_with (this ->_flag_prefix ))
644
+ return {
645
+ . type = detail::argument_token::t_flag_primary ,
646
+ . value = std::string (arg_value. substr ( this -> _primary_flag_prefix_length ))
647
+ } ;
633
648
634
- return false ;
649
+ if (arg_value.starts_with (this ->_flag_prefix_char ))
650
+ return {
651
+ .type = detail::argument_token::t_flag_secondary,
652
+ .value = std::string (arg_value.substr (this ->_secondary_flag_prefix_length ))
653
+ };
654
+
655
+ return {.type = detail::argument_token::t_value, .value = std::string (arg_value)};
656
+ }
657
+
658
+ /* *
659
+ * @brief Check if a flag token is valid based on its value.
660
+ * @param tok The processed argument token.
661
+ * @return true if the token's value matches an argument name specified within the parser, false otherwise.
662
+ */
663
+ [[nodiscard]] bool _is_valid_flag (const detail::argument_token& tok) const noexcept {
664
+ if (tok.type == detail::argument_token::t_flag_primary)
665
+ return this ->_is_arg_name_used ({tok.value }, detail::argument_name::m_primary);
666
+ else
667
+ return this ->_is_arg_name_used ({tok.value }, detail::argument_name::m_secondary);
635
668
}
636
669
637
670
/* *
@@ -658,22 +691,6 @@ class argument_parser {
658
691
}
659
692
}
660
693
661
- /* *
662
- * @brief Remove the flag prefix from the argument.
663
- * @param arg_flag The argument flag to strip the prefix from.
664
- * @return A flag argument token representing whether the prefix indicated a primary or a secondary flag.
665
- */
666
- detail::argument_token::token_type _strip_flag_prefix (std::string& arg_flag) const noexcept {
667
- if (arg_flag.starts_with (this ->_flag_prefix )) {
668
- arg_flag.erase (0 , this ->_primary_flag_prefix_length );
669
- return detail::argument_token::t_flag_primary;
670
- }
671
- else {
672
- arg_flag.erase (0 , this ->_secondary_flag_prefix_length );
673
- return detail::argument_token::t_flag_secondary;
674
- }
675
- }
676
-
677
694
/* *
678
695
* @brief Implementation of parsing command-line arguments.
679
696
* @param arg_tokens The list of command-line argument tokens.
0 commit comments