diff --git a/src/headers/parser/argument_parser.hpp b/src/headers/parser/argument_parser.hpp index da89814..c80b4d4 100644 --- a/src/headers/parser/argument_parser.hpp +++ b/src/headers/parser/argument_parser.hpp @@ -20,6 +20,32 @@ #include namespace argument_parser { + namespace internal::sfinae { + template struct has_format_hint { + private: + typedef char YesType[1]; + typedef char NoType[2]; + + template static YesType &test(decltype(&C::format_hint)); + template static NoType &test(...); + + public: + static constexpr bool value = sizeof(test(0)) == sizeof(YesType); + }; + + template struct has_purpose_hint { + private: + typedef char YesType[1]; + typedef char NoType[2]; + + template static YesType &test(decltype(&C::purpose_hint)); + template static NoType &test(...); + + public: + static constexpr bool value = sizeof(test(0)) == sizeof(YesType); + }; + } // namespace internal::sfinae + namespace internal::atomic { template class copyable_atomic { public: @@ -72,6 +98,7 @@ namespace argument_parser { [[nodiscard]] virtual bool expects_parameter() const = 0; virtual void invoke() const = 0; virtual void invoke_with_parameter(const std::string ¶m) const = 0; + [[nodiscard]] virtual std::pair get_trait_hints() const = 0; [[nodiscard]] virtual std::unique_ptr clone() const = 0; }; @@ -92,8 +119,31 @@ namespace argument_parser { } void invoke_with_parameter(const std::string ¶m) const override { - T parsed_value = parsing_traits::parser_trait::parse(param); - invoke(parsed_value); + bool parse_success = false; + try { + T parsed_value = parsing_traits::parser_trait::parse(param); + parse_success = true; + invoke(parsed_value); + } catch (const std::runtime_error &e) { + if (!parse_success) { + auto [format_hint, purpose_hint] = get_trait_hints(); + if (purpose_hint.empty()) + purpose_hint = "value"; + std::string error_text{"'" + param + "' is not a valid " + purpose_hint + " ${KEY}"}; + if (!format_hint.empty()) + error_text += "\nExpected format: " + format_hint; + throw std::runtime_error(error_text); + } + } + } + + [[nodiscard]] std::pair get_trait_hints() const override { + if constexpr (internal::sfinae::has_format_hint>::value && + internal::sfinae::has_purpose_hint>::value) { + return {parsing_traits::parser_trait::format_hint, parsing_traits::parser_trait::purpose_hint}; + } else { + return {"", "value"}; + } } [[nodiscard]] std::unique_ptr clone() const override { @@ -120,6 +170,10 @@ namespace argument_parser { invoke(); } + [[nodiscard]] std::pair get_trait_hints() const override { + return {"", ""}; + } + [[nodiscard]] std::unique_ptr clone() const override { return std::make_unique(handler); } diff --git a/src/headers/parser/parser_v2.hpp b/src/headers/parser/parser_v2.hpp index a67209c..57a5a5b 100644 --- a/src/headers/parser/parser_v2.hpp +++ b/src/headers/parser/parser_v2.hpp @@ -15,10 +15,6 @@ #include namespace argument_parser::v2 { - namespace internal { - static inline fake_parser fake_parser{}; - } - enum class add_argument_flags { ShortArgument, LongArgument, HelpText, Action, Required }; namespace flags { @@ -104,30 +100,6 @@ namespace argument_parser::v2 { } private: - template struct has_format_hint { - private: - typedef char YesType[1]; - typedef char NoType[2]; - - template static YesType &test(decltype(&C::format_hint)); - template static NoType &test(...); - - public: - static constexpr bool value = sizeof(test(0)) == sizeof(YesType); - }; - - template struct has_purpose_hint { - private: - typedef char YesType[1]; - typedef char NoType[2]; - - template static YesType &test(decltype(&C::purpose_hint)); - template static NoType &test(...); - - public: - static constexpr bool value = sizeof(test(0)) == sizeof(YesType); - }; - template void add_argument_impl(ArgsMap const &argument_pairs) { std::unordered_map found_params{ @@ -174,8 +146,8 @@ namespace argument_parser::v2 { switch (suggested_add) { case candidate_type::typed_action: if (help_text.empty()) { - if constexpr (has_format_hint>::value && - has_purpose_hint>::value) { + if constexpr (internal::sfinae::has_format_hint>::value && + internal::sfinae::has_purpose_hint>::value) { auto format_hint = parsing_traits::parser_trait::format_hint; auto purpose_hint = parsing_traits::parser_trait::purpose_hint; help_text = "Triggers action with " + std::string(purpose_hint) + " (" + @@ -190,8 +162,8 @@ namespace argument_parser::v2 { break; case candidate_type::store_other: if (help_text.empty()) { - if constexpr (has_format_hint>::value && - has_purpose_hint>::value) { + if constexpr (internal::sfinae::has_format_hint>::value && + internal::sfinae::has_purpose_hint>::value) { auto format_hint = parsing_traits::parser_trait::format_hint; auto purpose_hint = parsing_traits::parser_trait::purpose_hint; help_text = diff --git a/src/main.cpp b/src/main.cpp index 5422cd2..5901896 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,5 @@ -#include "headers/parser/parsing_traits/traits.hpp" +#include #include -#define ALLOW_DASH_FOR_WINDOWS 0 #include #include @@ -24,7 +23,7 @@ template <> struct argument_parser::parsing_traits::parser_trait { return {x, y}; } static constexpr argument_parser::parsing_traits::hint_type format_hint = "x,y"; - static constexpr argument_parser::parsing_traits::hint_type purpose_hint = "point coordinates"; + static constexpr argument_parser::parsing_traits::hint_type purpose_hint = "coordinates"; }; template <> struct argument_parser::parsing_traits::parser_trait { @@ -178,11 +177,9 @@ int v2Examples() { } int main() { - return v2Examples(); - try { + return v2Examples(); } catch (std::exception const &e) { - std::cerr << "Error: " << e.what() << std::endl; - return -1; + std::cout << e.what() << std::endl; } } \ No newline at end of file diff --git a/src/source/parser/argument_parser.cpp b/src/source/parser/argument_parser.cpp index 0c42b69..8c5f4a9 100644 --- a/src/source/parser/argument_parser.cpp +++ b/src/source/parser/argument_parser.cpp @@ -225,6 +225,17 @@ namespace argument_parser { } } + std::string replace_var(std::string text, const std::string &var_name, const std::string &value) { + std::string placeholder = "${" + var_name + "}"; + size_t pos = text.find(placeholder); + + while (pos != std::string::npos) { + text.replace(pos, placeholder.length(), value); + pos = text.find(placeholder, pos + value.length()); + } + return text; + } + void base_parser::invoke_arguments(std::unordered_map const &values_for_arguments, std::vector> &found_arguments, std::optional const &found_help) { @@ -244,7 +255,9 @@ namespace argument_parser { } value.set_invoked(true); } catch (const std::runtime_error &e) { - error_stream << "Argument " << key << " failed with: " << e.what() << "\n"; + std::string err{e.what()}; + err = replace_var(err, "KEY", "for " + key); + error_stream << "Error: " << err << "\n"; } } @@ -367,6 +380,7 @@ namespace argument_parser { } std::cerr << "\n"; display_help(convention_types); + std::exit(1); } }