From fea5042af73cac647c40a3b12f8b82f915f4ad97 Mon Sep 17 00:00:00 2001 From: killua Date: Tue, 7 Oct 2025 02:08:25 +0400 Subject: [PATCH] feat(convention) add windows style argument conventions. --- include/argparse | 3 +- include/conventions/base_convention.hpp | 2 + .../windows_argument_convention.hpp | 91 +++++++++++++++++++ include/parser/argument_parser.hpp | 6 ++ src/main.cpp | 17 +++- 5 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 include/conventions/implementations/windows_argument_convention.hpp diff --git a/include/argparse b/include/argparse index 60ba673..3270284 100644 --- a/include/argparse +++ b/include/argparse @@ -24,4 +24,5 @@ namespace argument_parser { #endif #include -#include \ No newline at end of file +#include +#include \ No newline at end of file diff --git a/include/conventions/base_convention.hpp b/include/conventions/base_convention.hpp index 67643ef..f115e72 100644 --- a/include/conventions/base_convention.hpp +++ b/include/conventions/base_convention.hpp @@ -9,6 +9,8 @@ namespace argument_parser::conventions { enum class argument_type { SHORT, LONG, + POSITIONAL, + INTERCHANGABLE, ERROR }; diff --git a/include/conventions/implementations/windows_argument_convention.hpp b/include/conventions/implementations/windows_argument_convention.hpp new file mode 100644 index 0000000..1c5af9d --- /dev/null +++ b/include/conventions/implementations/windows_argument_convention.hpp @@ -0,0 +1,91 @@ +#pragma once +#include "base_convention.hpp" +#include + +#ifndef WINDOWS_ARGUMENT_CONVENTION_HPP +#define WINDOWS_ARGUMENT_CONVENTION_HPP + +namespace argument_parser::conventions::implementations { + class windows_argument_convention : public base_convention { + public: + // Windows style options are prefixed with a slash ('/'). And there's no difference for short vs long options. + parsed_argument get_argument(std::string const& raw) const override { + if (raw.starts_with(long_prec())) + return { argument_type::INTERCHANGABLE, raw.substr(1) }; + else + return { argument_type::ERROR, "Windows standard requires non-positional arguments with a preceeding slash ('\\')." }; + } + + std::string extract_value(std::string const& /*raw*/) const override { + // In non-equal GNU, value comes in next token + throw std::runtime_error("No inline value in standard GNU convention."); + } + + bool requires_next_token() const override { + return true; + } + + std::string name() const override { + return "Windows style options"; + } + + std::string short_prec() const override { + return "/"; + } + + std::string long_prec() const override { + return "/"; + } + + static windows_argument_convention instance; + private: + windows_argument_convention() = default; + }; + + class windows_equal_argument_convention : public base_convention { + public: + // Windows style options are prefixed with a slash ('/'). And there's no difference for short vs long options. + parsed_argument get_argument(std::string const& raw) const override { + auto pos = raw.find('='); + auto arg = pos != std::string::npos ? raw.substr(0, pos) : raw; + if (arg.starts_with(long_prec())) + return { argument_type::INTERCHANGABLE, arg.substr(1) }; + else + return { argument_type::ERROR, "Windows standard requires non-positional arguments with a preceeding slash ('\\')." }; + } + + std::string extract_value(std::string const& raw) const override { + auto pos = raw.find('='); + if (pos == std::string::npos || pos + 1 >= raw.size()) + throw std::runtime_error("Expected value after '='."); + return raw.substr(pos + 1); + } + + bool requires_next_token() const override { + return false; + } + + std::string name() const override { + return "Windows style options (equal signed form)"; + } + + std::string short_prec() const override { + return "/"; + } + + std::string long_prec() const override { + return "/"; + } + + static windows_equal_argument_convention instance; + private: + windows_equal_argument_convention() = default; + }; +} + +namespace argument_parser::conventions { + static inline const implementations::windows_argument_convention windows_argument_convention = implementations::windows_argument_convention::instance; + static inline const implementations::windows_equal_argument_convention windows_equal_argument_convention = implementations::windows_equal_argument_convention::instance; +} + +#endif // WINDOWS_ARGUMENT_CONVENTION_HPP \ No newline at end of file diff --git a/include/parser/argument_parser.hpp b/include/parser/argument_parser.hpp index e5dfc74..98e9c83 100644 --- a/include/parser/argument_parser.hpp +++ b/include/parser/argument_parser.hpp @@ -194,12 +194,18 @@ namespace argument_parser { } argument& get_argument(conventions::parsed_argument const& arg) { + if (arg.first == conventions::argument_type::LONG) { auto long_pos = long_arguments.find(arg.second); if (long_pos != long_arguments.end()) return argument_map.at(long_pos->second); } else if (arg.first == conventions::argument_type::SHORT) { auto short_pos = short_arguments.find(arg.second); if (short_pos != short_arguments.end()) return argument_map.at(short_pos->second); + } else if (arg.first == conventions::argument_type::INTERCHANGABLE) { + auto long_pos = long_arguments.find(arg.second); + if (long_pos != long_arguments.end()) return argument_map.at(long_pos->second); + auto short_pos = short_arguments.find(arg.second); + if (short_pos != short_arguments.end()) return argument_map.at(short_pos->second); } throw std::runtime_error("Unknown argument: " + arg.second); } diff --git a/src/main.cpp b/src/main.cpp index 3bff33d..b342d28 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,3 @@ -#include "argument_parser.hpp" #include #include #include @@ -6,7 +5,6 @@ #include #include -using namespace argument_parser::conventions; struct Point { int x, y; @@ -69,8 +67,10 @@ struct argument_parser::parsing_traits::parser_trait> { }; const std::initializer_list conventions = { - &gnu_argument_convention, - &gnu_equal_argument_convention + &argument_parser::conventions::gnu_argument_convention, + &argument_parser::conventions::gnu_equal_argument_convention, + &argument_parser::conventions::windows_argument_convention, + &argument_parser::conventions::windows_equal_argument_convention }; const auto echo = argument_parser::helpers::make_parametered_action([](std::string const& text) { @@ -150,7 +150,14 @@ int main() { parser.add_argument>("t", "test", "Test vector", false); parser.add_argument>("ts", "test-strings", "Test vector", false); parser.on_complete(::run_grep); - parser.handle_arguments(conventions); + try { + parser.handle_arguments(conventions); + } catch(std::exception const& e) { + std::cerr << "Error: " << e.what() << std::endl; + parser.display_help(conventions); + return -1; + } + auto test = parser.get_optional>("test"); if (test) {