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>
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 {
template <typename T> 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 &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;
};
@@ -92,8 +119,31 @@ namespace argument_parser {
}
void invoke_with_parameter(const std::string &param) const override {
bool parse_success = false;
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 {
@@ -120,6 +170,10 @@ namespace argument_parser {
invoke();
}
[[nodiscard]] std::pair<std::string, std::string> get_trait_hints() const override {
return {"", ""};
}
[[nodiscard]] std::unique_ptr<action_base> clone() const override {
return std::make_unique<non_parametered_action>(handler);
}

View File

@@ -15,10 +15,6 @@
#include <vector>
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 <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>
void add_argument_impl(ArgsMap const &argument_pairs) {
std::unordered_map<extended_add_argument_flags, bool> 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<parsing_traits::parser_trait<T>>::value &&
has_purpose_hint<parsing_traits::parser_trait<T>>::value) {
if constexpr (internal::sfinae::has_format_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 purpose_hint = parsing_traits::parser_trait<T>::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<parsing_traits::parser_trait<T>>::value &&
has_purpose_hint<parsing_traits::parser_trait<T>>::value) {
if constexpr (internal::sfinae::has_format_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 purpose_hint = parsing_traits::parser_trait<T>::purpose_hint;
help_text =

View File

@@ -1,6 +1,5 @@
#include "headers/parser/parsing_traits/traits.hpp"
#include <exception>
#include <string>
#define ALLOW_DASH_FOR_WINDOWS 0
#include <argparse>
#include <fstream>
@@ -24,7 +23,7 @@ template <> struct argument_parser::parsing_traits::parser_trait<Point> {
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<std::regex> {
@@ -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;
}
}

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,
std::vector<std::pair<std::string, argument>> &found_arguments,
std::optional<argument> 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);
}
}