diff --git a/include/argparse.hpp b/include/argparse.hpp index 6f66acf..e13f88c 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -17,6 +17,7 @@ // 11. all api test // 12. top100 linux command test // 13. subcommand support +// 14. throw execption #include #include @@ -33,6 +34,29 @@ #include namespace argparse { + +class bad_value_access : public std::exception { + public: + explicit bad_value_access(std::string msg):msg(std::move(msg)) { } + const char* what() const noexcept override { + return msg.c_str(); + } + + private: + std::string msg; +}; + +class invalid_argument : public std::invalid_argument { + public: + invalid_argument(const char* msg): std::invalid_argument(msg){} + invalid_argument(std::string const& msg): std::invalid_argument(msg){} +}; + +template +class list_split_by; +template +class pair_split_by; + namespace { template @@ -42,10 +66,96 @@ template struct is_reference_wrapper> : std::true_type {}; template -using is_reference_wrapper_t = typename is_reference_wrapper::type; +inline constexpr bool is_reference_wrapper_v = is_reference_wrapper::value; template -inline constexpr bool is_reference_wrapper_v = is_reference_wrapper::value; +struct is_vector: std::false_type{}; +template +struct is_vector>: std::true_type{}; +template +inline constexpr bool is_vector_v = is_vector::value; + +template +struct is_pair: std::false_type{}; +template +struct is_pair>: std::true_type {}; +template +inline constexpr bool is_pair_v = is_pair::value; + +template +struct is_map: std::false_type{}; +template +struct is_map>: std::true_type {}; +template +inline constexpr bool is_map_v = is_map::value; + +template +struct is_need_split: std::false_type{}; +template +struct is_need_split>: std::true_type{}; +template +struct is_need_split>>: std::true_type{}; +template +struct is_need_split>: std::true_type{}; +template +struct is_need_split>>: std::true_type{}; +template +inline constexpr bool is_need_split_v = is_need_split::value; + +template +struct default_delimiter: std::integral_constant{}; +template +inline constexpr char default_delimiter_v = default_delimiter::value; + +template +struct is_list_split_by: std::false_type{}; +template +struct is_list_split_by>: std::true_type{}; +template +inline constexpr bool is_list_split_by_v = is_list_split_by::value; + +template +struct is_pair_split_by: std::false_type{}; +template +struct is_pair_split_by>: std::true_type{}; +template +inline constexpr bool is_pair_split_by_v = is_pair_split_by::value; + +template +struct is_transformable_type: std::false_type{}; + +template +struct is_transformable_type || + std::is_same_v || + std::is_same_v || + std::is_same_v +>>: std::true_type{}; + +template +struct is_transformable_type, std::enable_if_t< + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v +>>: std::true_type{}; + +template +struct is_transformable_type, std::enable_if_t< + (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) && + ( std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) +>>: std::true_type{}; + +template +struct is_transformable_type, std::enable_if_t< + (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) && + ( std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) +>>: std::true_type{}; + +template +inline constexpr bool is_transformable_type_v = is_transformable_type::value; template struct is_flag_bindable_value : std::false_type {}; @@ -56,8 +166,6 @@ struct is_flag_bindable_value< std::enable_if_t || std::is_same_v>> : std::true_type {}; -template -using is_flag_bindable_value_t = typename is_flag_bindable_value::type; template inline constexpr bool is_flag_bindable_value_v = is_flag_bindable_value::value; @@ -68,76 +176,40 @@ struct is_option_bindable_value : std::false_type {}; template struct is_option_bindable_value< T, - std::enable_if_t || std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v, T> || - std::is_same_v, T>>> + std::enable_if_t>> : std::true_type {}; template -using is_option_bindable_value_t = typename is_option_bindable_value::type; -template -inline constexpr bool is_option_bindable_value_v = - is_option_bindable_value::value; - -template -struct is_bindable_value : std::false_type {}; +struct is_option_bindable_value< + std::vector, + std::enable_if_t>> + : std::true_type {}; -template -struct is_bindable_value< - T, - std::enable_if_t || std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v, T> || - std::is_same_v, T>>> +template +struct is_option_bindable_value< + std::map, + std::enable_if_t && is_transformable_type_v>> : std::true_type {}; template -using is_bindable_value_t = typename is_bindable_value::type; -template -inline constexpr bool is_bindable_value_v = is_bindable_value::value; +inline constexpr bool is_option_bindable_value_v = + is_option_bindable_value::value; template struct is_position_bindable_value : std::false_type {}; template -struct is_position_bindable_value< - T, - std::enable_if_t || - std::is_same_v, T> || - std::is_same_v, T>>> - : std::true_type {}; +struct is_position_bindable_value>> : std::true_type {}; template -using is_position_bindable_value_t = - typename is_position_bindable_value::type; +struct is_position_bindable_value, std::enable_if_t>> : std::true_type {}; + template inline constexpr bool is_position_bindable_value_v = is_position_bindable_value::value; -// element_size_is_2 template -struct element_size_is_2 : public std::false_type {}; - -template -struct element_size_is_2> : public std::true_type {}; - -template -struct element_size_is_2> : public std::true_type {}; - -template -struct element_size_is_2> : public std::true_type {}; - -template -inline constexpr bool element_size_is_2_v = element_size_is_2::value; - -template -inline constexpr bool is_bindable_noncontainer_value_v = - std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v || - element_size_is_2_v; +inline constexpr bool is_bindable_value_v = is_flag_bindable_value_v || is_option_bindable_value_v || is_position_bindable_value_v; // overloaded template @@ -155,18 +227,36 @@ struct is_option_bindable_container : std::false_type {}; template struct is_option_bindable_container< std::vector, - std::enable_if_t>> : std::true_type {}; + std::enable_if_t>> : std::true_type {}; template struct is_option_bindable_container< std::map, - std::enable_if_t && - is_bindable_noncontainer_value_v>> : std::true_type {}; + std::enable_if_t>> : std::true_type {}; template constexpr bool is_option_bindable_container_v = is_option_bindable_container::value; + +template +struct merge_variant; +template +struct merge_variant, std::variant>{ using type = std::variant; }; +template +struct merge_variant, std::variant>{ using type = typename merge_variant, std::variant>::type; }; +template +struct make_variant_type { + using type1 = typename merge_variant, std::variant...>>::type; + using type2 = typename merge_variant...>, std::variant>...>>::type; + using type3 = typename merge_variant...>, std::variant>...>>::type; + using type4 = typename merge_variant>...>, std::variant>>...>>::type; + using type5 = typename merge_variant...>, std::variant>...>>::type; + + using type = typename merge_variant::type, type3>::type, type4>::type, type5>::type; +}; + + //********************************** namespace StringUtil { inline bool startswith(std::string const& str, std::string const& prefix) { @@ -215,112 +305,135 @@ inline bool is_position_arg(std::string const& str) { } // namespace StringUtil // bool -template , int> = 0> -std::tuple transform_value(std::string const& from) { +template , bool> = true> +void transform_value(std::string const& from, T& to) { std::string lower_from; std::transform(from.begin(), from.end(), std::back_inserter(lower_from), [](unsigned char c) { return std::tolower(c); }); if (lower_from == "true") { - return {0, "", true}; + to = true; } else if (lower_from == "false") { - return {0, "", false}; + to = false; } else { std::stringstream ss; - ss << "'" << from << "' must be one of 'true' or 'false'"; - return {1, ss.str(), false}; + ss << "'" << from << "' is neither 'true' nor 'false'"; + throw bad_value_access(ss.str()); } } -// int template && !std::is_same_v, - int> = 0> -std::tuple transform_value(std::string const& from) { - T to{}; + bool> = true> +void transform_value(std::string const& from, T& to) { auto [ptr, ec] = std::from_chars(from.data(), from.data() + from.size(), to); if (ec == std::errc::invalid_argument) { std::stringstream ss; ss << "'" << from << "' not invalid number"; - return {1, ss.str(), to}; + throw bad_value_access(ss.str()); } else if (ec != std::errc{}) { std::stringstream ss; ss << "'" << from << "' can't convert to a number"; - return {1, ss.str(), to}; + throw bad_value_access(ss.str()); } - return {0, "", to}; } -// double -template , int> = 0> -std::tuple transform_value(std::string const& from) { - std::size_t pos{}; - T to{std::stod(from, &pos)}; - if (pos == std::string::npos) { - return {0, "", to}; - } - { - std::stringstream ss; - ss << "'" << from << "' not invalid double value"; - return {1, ss.str(), to}; +template , bool> = true> +void transform_value(std::string const& from, T& to) { + try { + to = std::stod(from, nullptr); + } catch (std::invalid_argument const &e) { + throw invalid_argument(e.what()); } } // std::string -template , int> = 0> -std::tuple transform_value(std::string const& from) { - return {0, "", from}; +template , bool> = true> +void transform_value(std::string const& from, T& to) { + to = from; } -// std::pair, std::array, std::tuple -template , int> = 0> -std::tuple transform_value(std::string const& from) { - auto it = from.find('='); - if (it != std::string::npos) { - auto key_str = from.substr(0, it); - auto value_str = from.substr(it + 1); - auto [err_k, msg_k, val_k] = transform_value< - typename std::decay(std::declval()))>::type>( - key_str); - auto [err_v, msg_v, val_v] = transform_value< - typename std::decay(std::declval()))>::type>( - value_str); - return {0, "", {val_k, val_v}}; - } else { - std::stringstream ss; - ss << "'" << from << "' is not a '=' separated string"; - return {1, ss.str(), {}}; +template, bool> = true> +void transform_value(std::string const& from , T& to) { + to.split(from); +} + +template, bool> = true> +void transform_value(std::string const& from , T& to) { + to.split(from); +} + +template, bool> = true> +void transform_value(std::string const& from , T& to, char delimiter = '=') { + auto index = from.find(delimiter); + if (index != std::string::npos) { + transform_value(from.substr(0, index), to.first); + transform_value(from.substr(index+1), to.second); } } // container template , bool> = true> -std::pair insert_or_replace_value( +void insert_or_replace_value( T& bind_value, - std::string const& option_val) { + std::string const& option_val, char delimiter = '=') { + if (option_val.empty()) { + return; + } std::insert_iterator it(bind_value, bind_value.end()); // insert to end - auto [ec, error_msg, result] = - transform_value(option_val); - if (ec == 0) { + if constexpr (is_pair_v) { + using writable_pair = typename std::pair, typename T::value_type::second_type>; + writable_pair result; + transform_value(option_val, result, delimiter); + *it = std::move(result); + } else { + typename T::value_type result; + transform_value(option_val, result); *it = std::move(result); } - return {ec, error_msg}; } // non-container template , bool> = false> -std::pair insert_or_replace_value( +void insert_or_replace_value( T& bind_value, - std::string const& option_val) { - auto [ec, error_msg, result] = transform_value(option_val); - if (ec == 0) { - bind_value = std::move(result); + std::string const& option_val, char delimiter = '=') { + if (option_val.empty()) { + return; } - return {ec, error_msg}; + T result; + if constexpr (is_pair_v) { + transform_value(option_val, result, delimiter); + } else { + transform_value(option_val, result); + } + bind_value = std::move(result); } } // namespace +template +class list_split_by : public std::vector { + public: + void split(std::string const& from) { + auto ret = argparse::StringUtil::split(from, delim); + std::transform(begin(ret), end(ret), std::back_inserter(*this), [](std::string const& v) -> T { T ret{}; transform_value(v, ret); return ret;}); + } +}; + +template +class pair_split_by : public std::pair { + public: + void split(std::string const& from) { + auto index = from.find(delim); + if (index != std::string::npos) { + transform_value(from.substr(0, index), this->first); + transform_value(from.substr(index+1), this->second); + } + } +}; + + class Base { template class DefaultValueVisitor { @@ -334,14 +447,14 @@ class Base { decltype(default_value)>) { val.get() = default_value; } else { - assert(false); + throw std::bad_variant_access(); } } else { if constexpr (std::is_assignable_v) { val = default_value; } else { - assert(false); + throw std::bad_variant_access(); } } } @@ -352,10 +465,13 @@ class Base { friend class ArgParser; public: + enum class Type { FLAG, ALIAS_FLAG, OPTION, POSITIONAL }; virtual ~Base() = default; - [[nodiscard]] virtual bool is_flag() const = 0; - [[nodiscard]] virtual bool is_option() const = 0; - [[nodiscard]] virtual bool is_positional() const = 0; + Base::Type virtual type() const = 0; + bool is_flag() { return Type::FLAG == this->type() || is_alias_flag(); }; + bool is_alias_flag() { return Type::ALIAS_FLAG == this->type(); }; + bool is_option() { return Type::OPTION == this->type(); }; + bool is_positional() { return Type::POSITIONAL == this->type(); }; template >> T const& as() const { @@ -371,20 +487,20 @@ class Base { if constexpr (is_flag_bindable_value_v) { value = std::ref(bind_val); } else { - assert(false); + throw std::bad_variant_access(); } } else if (is_option()) { if constexpr (is_option_bindable_value_v) { value = std::ref(bind_val); } else { - assert(false); + throw std::bad_variant_access(); } assert(is_option_bindable_value_v); } else if (is_positional()) { if constexpr (is_position_bindable_value_v) { value = std::ref(bind_val); } else { - assert(false); + throw std::bad_variant_access(); } } return *this; @@ -424,10 +540,8 @@ class Base { virtual void hit(char short_name) = 0; virtual void hit(std::string const& long_name) = 0; - virtual std::pair hit(char short_name, - std::string const& val) = 0; - virtual std::pair hit(std::string const& long_name, - std::string const& val) = 0; + virtual void hit(char short_name, std::string const& val) = 0; + virtual void hit(std::string const& long_name, std::string const& val) = 0; [[nodiscard]] virtual std::string usage() const = 0; [[nodiscard]] virtual bool contains(std::string const& name) const { @@ -436,17 +550,17 @@ class Base { } std::vector flag_and_option_names; std::string help_msg; - std::variant, - std::reference_wrapper, - bool, - int, - std::reference_wrapper, - std::reference_wrapper>, - std::reference_wrapper>, - std::string, - std::vector, - std::map> - value; + using type2 = std::variant< + std::reference_wrapper>, + list_split_by<>, + std::reference_wrapper>, + pair_split_by<>, + std::reference_wrapper>>, + std::vector> + >; + using mytype = merge_variant::type, type2>::type; + + mytype value; int hit_count{0}; }; class ArgParser; @@ -454,8 +568,8 @@ class Flag : public Base { friend class ArgParser; template - struct ValueVisitor { - ValueVisitor(Flag const& obj, ShortOrLong const& flag) + struct FlagValueVisitor { + FlagValueVisitor(Flag const& obj, ShortOrLong const& flag) : flagObj{obj}, flag{flag} {} void operator()(bool& x) { if (flagObj.negate_contains(flag)) { @@ -511,9 +625,7 @@ class Flag : public Base { }; public: - [[nodiscard]] bool is_flag() const override { return true; }; - [[nodiscard]] bool is_option() const override { return false; }; - [[nodiscard]] bool is_positional() const override { return false; }; + Base::Type type() const override { return Base::Type::FLAG; }; protected: template hit(char const flag, - std::string const& val) override { - return hit(std::string(1, flag), val); + void hit(char const flag, std::string const& val) override { + hit(std::string(1, flag), val); } - std::pair hit(std::string const& flag, - std::string const& val) override { + void hit(std::string const& flag, std::string const& val) override { assert(false); - return {1, "flag not hold a value"}; } [[nodiscard]] std::string usage() const override { @@ -621,36 +730,38 @@ class Flag : public Base { template void hit_impl(ShortOrLong const& flag) { hit_count++; - std::visit(ValueVisitor(*this, flag), value); + std::visit(FlagValueVisitor(*this, flag), value); } private: std::vector negate_flag_names{}; }; -class Option : public Base { - friend class ArgParser; - - struct ValueVisitor { - explicit ValueVisitor(std::string const& opt_val) : opt_val(opt_val) {} - template , bool> = true> - std::pair operator()(T& x) { - return insert_or_replace_value(x, opt_val); - } - template , bool> = true> - std::pair operator()(std::reference_wrapper& x) { - return insert_or_replace_value(x.get(), opt_val); - } +class AliasFlag: public Flag { + friend class ArgParser; + public: + Base::Type type() const override { return Base::Type::ALIAS_FLAG; }; + protected: + std::unique_ptr static make_flag(std::string const& flag_desc, std::string const& option_name, std::string const& option_value) { + return std::unique_ptr(new AliasFlag(flag_desc, option_name, option_value)); + } + explicit AliasFlag(std::string const& flag_desc, std::string const& option_name, std::string const& option_value):Flag(flag_desc, Base::identity{}),option_name(option_name), option_value(option_value){} + void hit(char const flag) override { + Flag::hit(flag); + } + void hit(std::string const& flag) override { + Flag::hit(flag); + } - std::string const& opt_val; - }; + private: + std::string option_name; + std::string option_value; +}; +class Option : public Base { + friend class ArgParser; public: - [[nodiscard]] bool is_flag() const override { return false; }; - [[nodiscard]] bool is_option() const override { return true; }; - [[nodiscard]] bool is_positional() const override { return false; }; + Base::Type type() const override { return Base::Type::OPTION; }; protected: template hit(std::string const&, - std::string const& val) override { + void hit(std::string const&, std::string const& val) override { return hit_impl(val); } - std::pair hit(char, std::string const& val) override { + void hit(char, std::string const& val) override { return hit_impl(val); } void hit(std::string const& flag) override { assert(false); } @@ -739,22 +849,30 @@ class Option : public Base { } } - std::pair hit_impl(std::string const& opt_val) { + void hit_impl(std::string const& opt_val) { hit_count++; - return std::visit(ValueVisitor(opt_val), value); + std::visit( + [&opt_val, this](auto &x) { + using T = std::remove_reference_t; + if constexpr (is_reference_wrapper_v) { + insert_or_replace_value(x.get(), opt_val, delimiter); + } else { + insert_or_replace_value(x, opt_val, delimiter); + } + } + , value); } private: std::string val_help_msg; + char delimiter{'\0'}; }; class Positional : public Base { friend class ArgParser; public: - [[nodiscard]] bool is_flag() const override { return false; }; - [[nodiscard]] bool is_option() const override { return false; }; - [[nodiscard]] bool is_positional() const override { return true; }; + Base::Type type() const override { return Base::Type::POSITIONAL; }; protected: template @@ -770,46 +888,28 @@ class Positional : public Base { void hit(char short_name) override { assert(false); }; void hit(std::string const& long_name) override { assert(false); }; - std::pair hit(char short_name, + void hit(char short_name, std::string const& val) override { assert(false); - return {1, "flag not a value"}; }; - std::pair hit(std::string const& long_name, + void hit(std::string const& long_name, std::string const& val) override { std::visit( overloaded{[&val, this](auto& v) { using type = std::remove_reference_t; - if constexpr (std::is_same_v) { - v = val; - can_set_value = false; - } else if constexpr (std::is_same_v, type>) { - v.push_back(val); - can_set_value = true; - } else if constexpr (std::is_same_v< - std::map, type>) { - // TODO: - assert(false); - } else if constexpr (std::is_same_v< - std::reference_wrapper, type>) { - v.get() = val; - can_set_value = false; - } else if constexpr (std::is_same_v>, - type>) { - v.get().push_back(val); - can_set_value = true; - } else if constexpr (std::is_same_v>, - type>) { - // TODO: - assert(false); + if constexpr (is_reference_wrapper_v) { + insert_or_replace_value(v.get(), val, delimiter); + if constexpr (!is_option_bindable_container_v) { + can_set_value = false; + } } else { - assert(false); + insert_or_replace_value(v, val, delimiter); + if constexpr (!is_option_bindable_container_v) { + can_set_value = false; + } } }}, value); - return {0, ""}; }; [[nodiscard]] std::string usage() const override { return ""; }; @@ -827,6 +927,7 @@ class Positional : public Base { flag_and_option_names.push_back(name); } bool can_set_value{true}; + char delimiter{'\0'}; }; class ArgParser { @@ -847,13 +948,13 @@ class ArgParser { PositionValueVisitor(std::string const& opt_val) : opt_val(opt_val) {} template , bool> = true> - std::pair operator()(T& x) { - return insert_or_replace_value(x, opt_val); + void operator()(T& x) { + insert_or_replace_value(x, opt_val); } template , bool> = true> - std::pair operator()(std::reference_wrapper& x) { - return insert_or_replace_value(x.get(), opt_val); + void operator()(std::reference_wrapper& x) { + insert_or_replace_value(x.get(), opt_val); } std::string const& opt_val; @@ -886,9 +987,15 @@ class ArgParser { all_options.push_back(std::move(x)); return *p; } + AliasFlag& add_alias_flag(std::string const& flag_desc, std::string const& option_name, std::string const& option_value) { + auto x = AliasFlag::make_flag(flag_desc, option_name, option_value); + auto p = x.get(); + all_options.push_back(std::move(x)); + return *p; + } template >> + typename = std::enable_if_t && is_option_bindable_value_v>> Option& add_option(std::string const& option_desc, T& bind) { auto x = Option::make_option(option_desc, bind); auto p = x.get(); @@ -897,7 +1004,7 @@ class ArgParser { } template >> + typename = std::enable_if_t && is_option_bindable_value_v>> Option& add_option(std::string const& option_desc) { auto x = Option::make_option(option_desc); auto p = x.get(); @@ -906,7 +1013,27 @@ class ArgParser { } template >> + typename = std::enable_if_t && is_option_bindable_value_v>> + Option& add_option(std::string const& option_desc, T& bind, char delimiter = default_delimiter_v) { + auto x = Option::make_option(option_desc, bind); + auto p = x.get(); + all_options.push_back(std::move(x)); + p->delimiter = delimiter; + return *p; + } + + template && is_option_bindable_value_v>> + Option& add_option(std::string const& option_desc, char delimiter = default_delimiter_v) { + auto x = Option::make_option(option_desc); + auto p = x.get(); + all_options.push_back(std::move(x)); + p->delimiter = delimiter; + return *p; + } + + template && is_position_bindable_value_v>> Positional& add_positional(std::string const& name, T& bind) { auto x = Positional::make_positional(name, bind); auto p = x.get(); @@ -914,7 +1041,7 @@ class ArgParser { return *p; } template , - typename = std::enable_if_t>> + typename = std::enable_if_t && is_position_bindable_value_v>> Positional& add_positional(std::string const& name) { auto x = Positional::make_positional(name); auto p = x.get(); @@ -922,6 +1049,25 @@ class ArgParser { return *p; } + template && is_position_bindable_value_v>> + Positional& add_positional(std::string const& name, T& bind, char delimiter = default_delimiter_v) { + auto x = Positional::make_positional(name, bind); + auto p = x.get(); + all_options.push_back(std::move(x)); + p->delimiter = delimiter; + return *p; + } + template , + typename = std::enable_if_t && is_position_bindable_value_v>> + Positional& add_positional(std::string const& name, char delimiter = default_delimiter_v) { + auto x = Positional::make_positional(name); + auto p = x.get(); + all_options.push_back(std::move(x)); + p->delimiter = delimiter; + return *p; + } + std::string usage() { std::stringstream ss; ss << (program_name.empty() ? "?" : program_name) << " [OPTION]... "; @@ -1001,17 +1147,21 @@ class ArgParser { while (short_p != curr_arg.end()) { if (auto short_flag = get_flag(*short_p); short_flag.has_value()) { (*short_flag)->hit(*short_p); + if ((*short_flag)->is_alias_flag()) { + auto* alis_flag = dynamic_cast(*short_flag); + if (auto option = get_option(alis_flag->option_name); option.has_value()) { + option.value()->hit(alis_flag->option_name, alis_flag->option_value); + } else { + assert(false); + } + } short_p++; } else if (auto short_option = get_option(*short_p); short_option.has_value()) { if (short_p + 1 != curr_arg.end()) { - if (auto const [ret, err] = (*short_option) ->hit(*short_p, std::string(short_p + 1, curr_arg.end())); - ret != 0) { - return {ret, err}; - } short_p = curr_arg.end(); } else { if (next == command_line_args.end()) { @@ -1024,11 +1174,7 @@ class ArgParser { ss << "option requires an argument -- -" << *short_p; return {1, ss.str()}; } else { - if (auto const [ret, err] = (*short_option)->hit(*short_p, *next); - ret != 0) { - return {ret, err}; - } short_p = curr_arg.end(); next = std::next(next); } @@ -1050,11 +1196,7 @@ class ArgParser { if (auto i = curr_arg.find('=', 2); i != std::string::npos) { std::string option = curr_arg.substr(2, i - 2); if (auto long_opt = get_option(option); long_opt.has_value()) { - if (auto const [ret, msg] = (*long_opt)->hit(option, curr_arg.substr(i + 1)); - ret != 0) { - return {ret, msg}; - } } else { std::stringstream ss; ss << "invalid option -- --" << option; @@ -1074,10 +1216,7 @@ class ArgParser { ss << "option requires an argument -- --" << option; return {1, ss.str()}; } else { - if (auto const [ret, err] = (*long_opt)->hit(option, *next); - ret != 0) { - return {ret, err}; - } + (*long_opt)->hit(option, *next); next = std::next(next); } } else { diff --git a/tests/argparse_test.cpp b/tests/argparse_test.cpp index 77781eb..326adf0 100644 --- a/tests/argparse_test.cpp +++ b/tests/argparse_test.cpp @@ -222,7 +222,9 @@ TEST(ArgParser, parser0_3) { bool flag_2{false}; bool flag_3{false}; int flag_v{false}; + // TODO: std::map option_e; + // std::vector("m,max-count") + .help("stop after NUM selected lines"); + parser.add_flag("b,byte-offset") + .help("print the byte offset with output lines"); + parser.add_flag("n,line-number") + .help("print line number with output lines"); + parser.add_flag("line-buffered") + .help("flush output on every line"); + parser.add_flag("H,with-filename") + .help("print file name with output lines"); + parser.add_flag("h,no-filename") + .help("suppress the file name prefix on output"); + parser.add_option("label") + .help("use LABEL as the standard input file name prefix"); + parser.add_flag("o,only-matching") + .help("show only nonempty parts of lines that match"); + parser.add_flag("q,quiet,") + .help("--silent suppress all normal output"); + parser.add_option("binary-files") + .help("assume that binary files are TYPE; TYPE is 'binary', 'text', or 'without-match'"); + parser.add_alias_flag("a,text", "binary-files", "text") + .help("equivalent to --binary-files=text"); + parser.add_alias_flag("-I", "binary-files", "without-match") + .help("equivalent to --binary-files=without-match"); + parser.add_option("d,directories") + .help("how to handle directories; ACTION is 'read', 'recurse', or 'skip'"); + parser.add_option("D,devices") + .help("how to handle devices, FIFOs and sockets; ACTION is 'read' or 'skip'"); + parser.add_flag("r,recursive") + .help("like --directories=recurse"); + parser.add_flag("R,dereference-recursive") + .help("likewise, but follow all symlinks"); + parser.add_option("include") + .help("search only files that match GLOB (a file pattern)"); + parser.add_option("exclude") + .help("skip files that match GLOB"); + parser.add_option("exclude-from") + .help("skip files that match any file pattern from FILE"); + parser.add_option("exclude-dir") + .help("skip directories that match GLOB"); + parser.add_flag("L,files-without-match") + .help("print only names of FILEs with no selected lines"); + parser.add_flag("l,files-with-matches") + .help("print only names of FILEs with selected lines"); + parser.add_flag("c,count") + .help("print only a count of selected lines per FILE"); + parser.add_flag("T,initial-tab") + .help("make tabs line up (if needed)"); + parser.add_flag("Z,null") + .help("print 0 byte after FILE name"); + + parser.add_option("B,before-context") + .help("print NUM lines of leading context"); + parser.add_option("A,after-context") + .help("print NUM lines of trailing context"); + parser.add_option("C,context") + .help("print NUM lines of output context"); + // TODO: + // -NUM same as --context=NUM + parser.add_option("group-separator") + .help("print SEP on line between matches with context"); + parser.add_option("no-group-separator") + .help("do not print separator for matches with context"); + parser.add_option("color,colour") + .help("use markers to highlight the matching strings; WHEN is 'always', 'never', or 'auto'"); + parser.add_option("U,binary") + .help("do not strip CR characters at EOL (MSDOS/Windows)"); + + return parser; +} + + + argparse::ArgParser make_zip_parser(){ argparse::ArgParser parser; return parser; diff --git a/tests/ls_test.cpp b/tests/ls_test.cpp index 31bd331..fa88ab2 100644 --- a/tests/ls_test.cpp +++ b/tests/ls_test.cpp @@ -227,7 +227,6 @@ struct LsOptions { bool flag_C; std::string option_D; bool flag_F; - bool flag_G; bool flag_H; bool flag_I; bool flag_L; @@ -297,7 +296,7 @@ make_ls_parser_bind() { .help( R"(Display a slash (‘/’) immediately after each pathname that is a directory, an asterisk (‘*’) after each that is executable, an at sign (‘@’) after each symbolic link, an equals sign (‘=’) after each socket, a percent sign (‘%’) after each whiteout, and a vertical bar (‘|’) after each that is a FIFO.)"); - parser.add_flag("G", option->flag_G) + parser.add_alias_flag("G", "color", "auto") .help( R"(Enable colorized output. This option is equivalent to defining CLICOLOR or COLORTERM in the environment and setting --color=auto. (See below.) This functionality can be compiled out by removing the definition of COLORLS. This option is not defined in IEEE Std 1003.1-2008 (“POSIX.1”).)"); @@ -492,7 +491,7 @@ argparse::ArgParser make_ls_parser_unbind() { parser.add_flag("F").help( R"(Display a slash (‘/’) immediately after each pathname that is a directory, an asterisk (‘*’) after each that is executable, an at sign (‘@’) after each symbolic link, an equals sign (‘=’) after each socket, a percent sign (‘%’) after each whiteout, and a vertical bar (‘|’) after each that is a FIFO.)"); - parser.add_flag("G").help( + parser.add_alias_flag("G", "color", "auto").help( R"(Enable colorized output. This option is equivalent to defining CLICOLOR or COLORTERM in the environment and setting --color=auto. (See below.) This functionality can be compiled out by removing the definition of COLORLS. This option is not defined in IEEE Std 1003.1-2008 (“POSIX.1”).)"); parser.add_flag("H").help( @@ -730,7 +729,6 @@ TEST(ArgParse, lscmd_bind) { ASSERT_EQ(parser["B"].as(), options->flag_B); ASSERT_EQ(parser["C"].as(), options->flag_C); ASSERT_EQ(parser["F"].as(), options->flag_F); - ASSERT_EQ(parser["G"].as(), options->flag_G); ASSERT_EQ(parser["H"].as(), options->flag_H); ASSERT_EQ(parser["I"].as(), options->flag_I); ASSERT_EQ(parser["L"].as(), options->flag_L); @@ -780,7 +778,6 @@ TEST(ArgParse, lscmd_bind) { ASSERT_EQ(&(parser["B"].as()), &(options->flag_B)); ASSERT_EQ(&(parser["C"].as()), &(options->flag_C)); ASSERT_EQ(&(parser["F"].as()), &(options->flag_F)); - ASSERT_EQ(&(parser["G"].as()), &(options->flag_G)); ASSERT_EQ(&(parser["H"].as()), &(options->flag_H)); ASSERT_EQ(&(parser["I"].as()), &(options->flag_I)); ASSERT_EQ(&(parser["L"].as()), &(options->flag_L)); @@ -1003,7 +1000,7 @@ TEST(lscmd, parser3_bind) { ASSERT_EQ(parser["color"].as(), "when"); ASSERT_EQ(options->option_color, "when"); - std::vector cmd{"ls", "-laF", "--color=always", + std::vector cmd{"ls", "-laF", "--color=always", "-G", "./1", "./1/2/3", ".hided/"}; parser.parse(cmd.size(), cmd.data()); @@ -1015,8 +1012,8 @@ TEST(lscmd, parser3_bind) { ASSERT_TRUE(options->flag_a); ASSERT_TRUE(options->flag_F); - ASSERT_EQ(parser["color"].as(), "always"); - ASSERT_EQ(options->option_color, "always"); + ASSERT_EQ(parser["color"].as(), "auto"); + ASSERT_EQ(options->option_color, "auto"); ASSERT_EQ( parser["files_or_directories"].as>().size(), 3); @@ -1038,7 +1035,7 @@ TEST(lscmd, parser3) { ASSERT_EQ(parser["color"].as(), "when"); - std::vector cmd{"ls", "-laF", "--color=always", + std::vector cmd{"ls", "-laF", "--color=always", "-G", "./1", "./1/2/3", ".hided/"}; parser.parse(cmd.size(), cmd.data()); @@ -1047,7 +1044,7 @@ TEST(lscmd, parser3) { ASSERT_TRUE(parser["a"].as()); ASSERT_TRUE(parser["F"].as()); - ASSERT_EQ(parser["color"].as(), "always"); + ASSERT_EQ(parser["color"].as(), "auto"); ASSERT_EQ( parser["files_or_directories"].as>().size(), 3);