chore: improve error generation. exit on missing required arg. remove redundant fake_parser.

This commit is contained in:
2026-03-16 22:37:59 +04:00
parent 593b7ed046
commit 9d108df846
4 changed files with 79 additions and 42 deletions

View File

@@ -20,6 +20,32 @@
#include <vector> #include <vector>
namespace argument_parser { namespace argument_parser {
namespace internal::sfinae {
template <typename T> struct has_format_hint {
private:
typedef char YesType[1];
typedef char NoType[2];
template <typename C> static YesType &test(decltype(&C::format_hint));
template <typename> static NoType &test(...);
public:
static constexpr bool value = sizeof(test<T>(0)) == sizeof(YesType);
};
template <typename T> struct has_purpose_hint {
private:
typedef char YesType[1];
typedef char NoType[2];
template <typename C> static YesType &test(decltype(&C::purpose_hint));
template <typename> static NoType &test(...);
public:
static constexpr bool value = sizeof(test<T>(0)) == sizeof(YesType);
};
} // namespace internal::sfinae
namespace internal::atomic { namespace internal::atomic {
template <typename T> class copyable_atomic { template <typename T> class copyable_atomic {
public: public:
@@ -72,6 +98,7 @@ namespace argument_parser {
[[nodiscard]] virtual bool expects_parameter() const = 0; [[nodiscard]] virtual bool expects_parameter() const = 0;
virtual void invoke() const = 0; virtual void invoke() const = 0;
virtual void invoke_with_parameter(const std::string &param) const = 0; virtual void invoke_with_parameter(const std::string &param) const = 0;
[[nodiscard]] virtual std::pair<std::string, std::string> get_trait_hints() const = 0;
[[nodiscard]] virtual std::unique_ptr<action_base> clone() const = 0; [[nodiscard]] virtual std::unique_ptr<action_base> clone() const = 0;
}; };
@@ -92,8 +119,31 @@ namespace argument_parser {
} }
void invoke_with_parameter(const std::string &param) const override { void invoke_with_parameter(const std::string &param) const override {
T parsed_value = parsing_traits::parser_trait<T>::parse(param); bool parse_success = false;
invoke(parsed_value); try {
T parsed_value = parsing_traits::parser_trait<T>::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<std::string, std::string> get_trait_hints() const override {
if constexpr (internal::sfinae::has_format_hint<parsing_traits::parser_trait<T>>::value &&
internal::sfinae::has_purpose_hint<parsing_traits::parser_trait<T>>::value) {
return {parsing_traits::parser_trait<T>::format_hint, parsing_traits::parser_trait<T>::purpose_hint};
} else {
return {"", "value"};
}
} }
[[nodiscard]] std::unique_ptr<action_base> clone() const override { [[nodiscard]] std::unique_ptr<action_base> clone() const override {
@@ -120,6 +170,10 @@ namespace argument_parser {
invoke(); invoke();
} }
[[nodiscard]] std::pair<std::string, std::string> get_trait_hints() const override {
return {"", ""};
}
[[nodiscard]] std::unique_ptr<action_base> clone() const override { [[nodiscard]] std::unique_ptr<action_base> clone() const override {
return std::make_unique<non_parametered_action>(handler); return std::make_unique<non_parametered_action>(handler);
} }

View File

@@ -15,10 +15,6 @@
#include <vector> #include <vector>
namespace argument_parser::v2 { namespace argument_parser::v2 {
namespace internal {
static inline fake_parser fake_parser{};
}
enum class add_argument_flags { ShortArgument, LongArgument, HelpText, Action, Required }; enum class add_argument_flags { ShortArgument, LongArgument, HelpText, Action, Required };
namespace flags { namespace flags {
@@ -104,30 +100,6 @@ namespace argument_parser::v2 {
} }
private: private:
template <typename T> struct has_format_hint {
private:
typedef char YesType[1];
typedef char NoType[2];
template <typename C> static YesType &test(decltype(&C::format_hint));
template <typename> static NoType &test(...);
public:
static constexpr bool value = sizeof(test<T>(0)) == sizeof(YesType);
};
template <typename T> struct has_purpose_hint {
private:
typedef char YesType[1];
typedef char NoType[2];
template <typename C> static YesType &test(decltype(&C::purpose_hint));
template <typename> static NoType &test(...);
public:
static constexpr bool value = sizeof(test<T>(0)) == sizeof(YesType);
};
template <bool IsTyped, typename ActionType, typename T, typename ArgsMap> template <bool IsTyped, typename ActionType, typename T, typename ArgsMap>
void add_argument_impl(ArgsMap const &argument_pairs) { void add_argument_impl(ArgsMap const &argument_pairs) {
std::unordered_map<extended_add_argument_flags, bool> found_params{ std::unordered_map<extended_add_argument_flags, bool> found_params{
@@ -174,8 +146,8 @@ namespace argument_parser::v2 {
switch (suggested_add) { switch (suggested_add) {
case candidate_type::typed_action: case candidate_type::typed_action:
if (help_text.empty()) { if (help_text.empty()) {
if constexpr (has_format_hint<parsing_traits::parser_trait<T>>::value && if constexpr (internal::sfinae::has_format_hint<parsing_traits::parser_trait<T>>::value &&
has_purpose_hint<parsing_traits::parser_trait<T>>::value) { internal::sfinae::has_purpose_hint<parsing_traits::parser_trait<T>>::value) {
auto format_hint = parsing_traits::parser_trait<T>::format_hint; auto format_hint = parsing_traits::parser_trait<T>::format_hint;
auto purpose_hint = parsing_traits::parser_trait<T>::purpose_hint; auto purpose_hint = parsing_traits::parser_trait<T>::purpose_hint;
help_text = "Triggers action with " + std::string(purpose_hint) + " (" + help_text = "Triggers action with " + std::string(purpose_hint) + " (" +
@@ -190,8 +162,8 @@ namespace argument_parser::v2 {
break; break;
case candidate_type::store_other: case candidate_type::store_other:
if (help_text.empty()) { if (help_text.empty()) {
if constexpr (has_format_hint<parsing_traits::parser_trait<T>>::value && if constexpr (internal::sfinae::has_format_hint<parsing_traits::parser_trait<T>>::value &&
has_purpose_hint<parsing_traits::parser_trait<T>>::value) { internal::sfinae::has_purpose_hint<parsing_traits::parser_trait<T>>::value) {
auto format_hint = parsing_traits::parser_trait<T>::format_hint; auto format_hint = parsing_traits::parser_trait<T>::format_hint;
auto purpose_hint = parsing_traits::parser_trait<T>::purpose_hint; auto purpose_hint = parsing_traits::parser_trait<T>::purpose_hint;
help_text = help_text =

View File

@@ -1,6 +1,5 @@
#include "headers/parser/parsing_traits/traits.hpp" #include <exception>
#include <string> #include <string>
#define ALLOW_DASH_FOR_WINDOWS 0
#include <argparse> #include <argparse>
#include <fstream> #include <fstream>
@@ -24,7 +23,7 @@ template <> struct argument_parser::parsing_traits::parser_trait<Point> {
return {x, y}; return {x, y};
} }
static constexpr argument_parser::parsing_traits::hint_type format_hint = "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<std::regex> { template <> struct argument_parser::parsing_traits::parser_trait<std::regex> {
@@ -178,11 +177,9 @@ int v2Examples() {
} }
int main() { int main() {
return v2Examples();
try { try {
return v2Examples();
} catch (std::exception const &e) { } catch (std::exception const &e) {
std::cerr << "Error: " << e.what() << std::endl; std::cout << e.what() << std::endl;
return -1;
} }
} }

View File

@@ -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<std::string, std::string> const &values_for_arguments, void base_parser::invoke_arguments(std::unordered_map<std::string, std::string> const &values_for_arguments,
std::vector<std::pair<std::string, argument>> &found_arguments, std::vector<std::pair<std::string, argument>> &found_arguments,
std::optional<argument> const &found_help) { std::optional<argument> const &found_help) {
@@ -244,7 +255,9 @@ namespace argument_parser {
} }
value.set_invoked(true); value.set_invoked(true);
} catch (const std::runtime_error &e) { } 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"; std::cerr << "\n";
display_help(convention_types); display_help(convention_types);
std::exit(1);
} }
} }