From a1dc3c0149fadfe60aa1d676e195d5a30d9bbb4a Mon Sep 17 00:00:00 2001 From: killua Date: Sun, 15 Mar 2026 22:55:23 +0400 Subject: [PATCH] feat: Introduce a modular argument convention system for GNU and Windows styles, add .clang-format, and update CMake configuration to C++17. --- .clang-format | 12 ++ CMakeLists.txt | 14 +- compile_commands.json | 14 +- include/conventions/base_convention.hpp | 49 ------- .../gnu_argument_convention.hpp | 98 -------------- .../windows_argument_convention.hpp | 120 ------------------ {include => src/headers}/argparse | 6 +- src/headers/conventions/base_convention.hpp | 35 +++++ .../gnu_argument_convention.hpp | 53 ++++++++ .../windows_argument_convention.hpp | 62 +++++++++ .../headers}/parser/argument_parser.hpp | 0 .../headers}/parser/fake_parser.hpp | 0 {include => src/headers}/parser/parser_v2.hpp | 0 .../headers}/parser/parsing_traits/traits.hpp | 0 .../parser/platform_headers/linux_parser.hpp | 0 .../parser/platform_headers/macos_parser.hpp | 0 .../platform_headers/windows_parser.hpp | 0 src/source/conventions/base_convention.cpp | 14 ++ .../gnu_argument_convention.cpp | 75 +++++++++++ .../windows_argument_convention.cpp | 103 +++++++++++++++ 20 files changed, 371 insertions(+), 284 deletions(-) create mode 100644 .clang-format delete mode 100644 include/conventions/base_convention.hpp delete mode 100644 include/conventions/implementations/gnu_argument_convention.hpp delete mode 100644 include/conventions/implementations/windows_argument_convention.hpp rename {include => src/headers}/argparse (81%) create mode 100644 src/headers/conventions/base_convention.hpp create mode 100644 src/headers/conventions/implementations/gnu_argument_convention.hpp create mode 100644 src/headers/conventions/implementations/windows_argument_convention.hpp rename {include => src/headers}/parser/argument_parser.hpp (100%) rename {include => src/headers}/parser/fake_parser.hpp (100%) rename {include => src/headers}/parser/parser_v2.hpp (100%) rename {include => src/headers}/parser/parsing_traits/traits.hpp (100%) rename {include => src/headers}/parser/platform_headers/linux_parser.hpp (100%) rename {include => src/headers}/parser/platform_headers/macos_parser.hpp (100%) rename {include => src/headers}/parser/platform_headers/windows_parser.hpp (100%) create mode 100644 src/source/conventions/base_convention.cpp create mode 100644 src/source/conventions/implementations/gnu_argument_convention.cpp create mode 100644 src/source/conventions/implementations/windows_argument_convention.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..20865fd --- /dev/null +++ b/.clang-format @@ -0,0 +1,12 @@ +--- +Language: Cpp +BasedOnStyle: LLVM +UseTab: Always +TabWidth: 4 +IndentWidth: 4 +NamespaceIndentation: All +AccessModifierOffset: -4 +BreakBeforeBraces: Attach +AllowShortFunctionsOnASingleLine: Empty +ColumnLimit: 120 +... diff --git a/CMakeLists.txt b/CMakeLists.txt index b24122e..fc3a00b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,18 +2,18 @@ cmake_minimum_required(VERSION 3.15) project(argument_parser) -set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_Release ${CMAKE_CURRENT_SOURCE_DIR}/bin/release) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_Debug ${CMAKE_CURRENT_SOURCE_DIR}/bin/debug) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -include_directories(include) -include_directories(include/parser) -include_directories(include/conventions) -include_directories(include/conventions/implementations) -include_directories(include/parser/platform_headers) -include_directories(include/parser/parsing_traits) +include_directories(src/headers) +include_directories(src/headers/parser) +include_directories(src/headers/conventions) +include_directories(src/headers/conventions/implementations) +include_directories(src/headers/parser/platform_headers) +include_directories(src/headers/parser/parsing_traits) add_executable(test src/main.cpp) \ No newline at end of file diff --git a/compile_commands.json b/compile_commands.json index 7ec9780..0664a7f 100644 --- a/compile_commands.json +++ b/compile_commands.json @@ -1,8 +1,8 @@ [ -{ - "directory": "/Users/killua/Projects/argument-parser/build", - "command": "/usr/bin/clang++ -I/Users/killua/Projects/argument-parser/include -I/Users/killua/Projects/argument-parser/include/parser -I/Users/killua/Projects/argument-parser/include/conventions -I/Users/killua/Projects/argument-parser/include/conventions/implementations -I/Users/killua/Projects/argument-parser/include/parser/platform_headers -I/Users/killua/Projects/argument-parser/include/parser/parsing_traits -g -std=gnu++2b -arch arm64 -o CMakeFiles/test.dir/src/main.cpp.o -c /Users/killua/Projects/argument-parser/src/main.cpp", - "file": "/Users/killua/Projects/argument-parser/src/main.cpp", - "output": "/Users/killua/Projects/argument-parser/build/CMakeFiles/test.dir/src/main.cpp.o" -} -] + { + "directory": "C:/Users/samet/Documents/CPP/argparser/argument-parser/build", + "command": "C:\\PROGRA~2\\MICROS~2\\18\\BUILDT~1\\VC\\Tools\\MSVC\\1450~1.357\\bin\\Hostx64\\x64\\cl.exe /nologo /TP -IC:\\Users\\samet\\Documents\\CPP\\argparser\\argument-parser\\src\\headers -IC:\\Users\\samet\\Documents\\CPP\\argparser\\argument-parser\\src\\headers\\parser -IC:\\Users\\samet\\Documents\\CPP\\argparser\\argument-parser\\src\\headers\\conventions -IC:\\Users\\samet\\Documents\\CPP\\argparser\\argument-parser\\src\\headers\\conventions\\implementations -IC:\\Users\\samet\\Documents\\CPP\\argparser\\argument-parser\\src\\headers\\parser\\platform_headers -IC:\\Users\\samet\\Documents\\CPP\\argparser\\argument-parser\\src\\headers\\parser\\parsing_traits /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -std:c++17 -MDd /FoCMakeFiles\\test.dir\\src\\main.cpp.obj /FdCMakeFiles\\test.dir\\ /FS -c C:\\Users\\samet\\Documents\\CPP\\argparser\\argument-parser\\src\\main.cpp", + "file": "C:/Users/samet/Documents/CPP/argparser/argument-parser/src/main.cpp", + "output": "C:/Users/samet/Documents/CPP/argparser/argument-parser/build/CMakeFiles/test.dir/src/main.cpp.obj" + } +] \ No newline at end of file diff --git a/include/conventions/base_convention.hpp b/include/conventions/base_convention.hpp deleted file mode 100644 index 796e4ac..0000000 --- a/include/conventions/base_convention.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once -#include -#include -#include - -#ifndef BASE_CONVENTION_HPP -#define BASE_CONVENTION_HPP - -namespace argument_parser::conventions { - enum class argument_type { - SHORT, - LONG, - POSITIONAL, - INTERCHANGABLE, - ERROR - }; - - using parsed_argument = std::pair; - - class base_convention { - public: - virtual std::string extract_value(std::string const&) const = 0; - virtual parsed_argument get_argument(std::string const&) const = 0; - virtual bool requires_next_token() const = 0; - virtual std::string name() const = 0; - virtual std::string short_prec() const = 0; - virtual std::string long_prec() const = 0; - protected: - base_convention() = default; - ~base_convention() = default; - }; - using convention = base_convention; -} - -namespace argument_parser::conventions::helpers { - static std::string to_lower(std::string s) { - std::transform(s.begin(), s.end(), s.begin(), - [](unsigned char c) { return std::tolower(c); }); - return s; - } - - static std::string to_upper(std::string s) { - std::transform(s.begin(), s.end(), s.begin(), - [](unsigned char c) { return std::toupper(c); }); - return s; - } -} - -#endif \ No newline at end of file diff --git a/include/conventions/implementations/gnu_argument_convention.hpp b/include/conventions/implementations/gnu_argument_convention.hpp deleted file mode 100644 index 50064ba..0000000 --- a/include/conventions/implementations/gnu_argument_convention.hpp +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once -#include "base_convention.hpp" -#include - -#ifndef GNU_ARGUMENT_CONVENTION_HPP -#define GNU_ARGUMENT_CONVENTION_HPP - -namespace argument_parser::conventions::implementations { - - class gnu_argument_convention : public base_convention { - public: - parsed_argument get_argument(std::string const& raw) const override { - if (raw.starts_with(long_prec())) - return {argument_type::LONG, raw.substr(2)}; - else if (raw.starts_with(short_prec())) - return {argument_type::SHORT, raw.substr(1)}; - else - return {argument_type::ERROR, "GNU standard convention does not allow arguments without a preceding dash."}; - } - - 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 "GNU-style long options"; - } - - std::string short_prec() const override { - return "-"; - } - - std::string long_prec() const override { - return "--"; - } - - static gnu_argument_convention instance; - private: - gnu_argument_convention() = default; - }; - - class gnu_equal_argument_convention : public base_convention { - public: - 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::LONG, arg.substr(2) }; - else if (arg.starts_with(short_prec())) - return {argument_type::SHORT, arg.substr(1) }; - else - return {argument_type::ERROR, "GNU standard convention does not allow arguments without a preceding dash."}; - } - - 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 "GNU-style long options (equal signed form)"; - } - - std::string short_prec() const override { - return "-"; - } - - std::string long_prec() const override { - return "--"; - } - - static gnu_equal_argument_convention instance; - private: - gnu_equal_argument_convention() = default; - }; - - inline gnu_argument_convention gnu_argument_convention::instance{}; - inline gnu_equal_argument_convention gnu_equal_argument_convention::instance{}; -} - -namespace argument_parser::conventions { - static inline const implementations::gnu_argument_convention gnu_argument_convention = implementations::gnu_argument_convention::instance; - static inline const implementations::gnu_equal_argument_convention gnu_equal_argument_convention = implementations::gnu_equal_argument_convention::instance; -} - - -#endif \ No newline at end of file diff --git a/include/conventions/implementations/windows_argument_convention.hpp b/include/conventions/implementations/windows_argument_convention.hpp deleted file mode 100644 index ab8f9c6..0000000 --- a/include/conventions/implementations/windows_argument_convention.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once -#include "base_convention.hpp" -#include - -#ifndef WINDOWS_ARGUMENT_CONVENTION_HPP -#define WINDOWS_ARGUMENT_CONVENTION_HPP - -#ifndef ALLOW_DASH_FOR_WINDOWS -#define ALLOW_DASH_FOR_WINDOWS 1 -#endif - -namespace argument_parser::conventions::implementations { - class windows_argument_convention : public base_convention { - public: - explicit windows_argument_convention(bool accept_dash = true) - : accept_dash_(accept_dash) { - } - - parsed_argument get_argument(std::string const& raw) const override { - if (raw.empty()) { - return { argument_type::ERROR, "Empty argument token." }; - } - const char c0 = raw[0]; - const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-'); - if (!ok_prefix) { - return { argument_type::ERROR, - accept_dash_ - ? "Windows-style expects options to start with '/' (or '-' in compat mode)." - : "Windows-style expects options to start with '/'." }; - } - - if (raw.find_first_of("=:") != std::string::npos) { - return { argument_type::ERROR, - "Inline values are not allowed in this convention; provide the value in the next token." }; - } - - std::string name = helpers::to_lower(raw.substr(1)); - if (name.empty()) { - return { argument_type::ERROR, "Option name cannot be empty after '/'." }; - } - - return { argument_type::INTERCHANGABLE, std::move(name) }; - } - - std::string extract_value(std::string const& /*raw*/) const override { - throw std::runtime_error("No inline value; value must be provided in the next token."); - } - - bool requires_next_token() const override { return true; } - - std::string name() const override { return "Windows style options (next-token values)"; } - std::string short_prec() const override { return accept_dash_ ? "-" : "/"; } - std::string long_prec() const override { return "/"; } - - static windows_argument_convention instance; - private: - bool accept_dash_; - }; - - class windows_kv_argument_convention : public base_convention { - public: - explicit windows_kv_argument_convention(bool accept_dash = true) - : accept_dash_(accept_dash) { - } - - parsed_argument get_argument(std::string const& raw) const override { - if (raw.empty()) { - return { argument_type::ERROR, "Empty argument token." }; - } - const char c0 = raw[0]; - const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-'); - if (!ok_prefix) { - return { argument_type::ERROR, - accept_dash_ - ? "Windows-style expects options to start with '/' (or '-' in compat mode)." - : "Windows-style expects options to start with '/'." }; - } - - const std::size_t sep = raw.find_first_of("=:"); - if (sep == std::string::npos) { - return { argument_type::ERROR, - "Expected an inline value using '=' or ':' (e.g., /opt=value or /opt:value)." }; - } - if (sep == 1) { - return { argument_type::ERROR, "Option name cannot be empty before '=' or ':'." }; - } - - std::string name = helpers::to_lower(raw.substr(1, sep - 1)); - return { argument_type::INTERCHANGABLE, std::move(name) }; - } - - std::string extract_value(std::string const& raw) const override { - const std::size_t sep = raw.find_first_of("=:"); - if (sep == std::string::npos || sep + 1 >= raw.size()) - throw std::runtime_error("Expected a value after '=' or ':'."); - return raw.substr(sep + 1); - } - - bool requires_next_token() const override { return false; } - - std::string name() const override { return "Windows style options (inline values via '=' or ':')"; } - std::string short_prec() const override { return accept_dash_ ? "-" : "/"; } - std::string long_prec() const override { return "/"; } - - static windows_kv_argument_convention instance; - private: - bool accept_dash_; - }; - - - inline windows_argument_convention windows_argument_convention::instance = windows_argument_convention(bool(ALLOW_DASH_FOR_WINDOWS)); - inline windows_kv_argument_convention windows_kv_argument_convention::instance = windows_kv_argument_convention(bool(ALLOW_DASH_FOR_WINDOWS)); -} - -namespace argument_parser::conventions { - static inline const implementations::windows_argument_convention windows_argument_convention = implementations::windows_argument_convention::instance; - static inline const implementations::windows_kv_argument_convention windows_equal_argument_convention = implementations::windows_kv_argument_convention::instance; -} - -#endif // WINDOWS_ARGUMENT_CONVENTION_HPP \ No newline at end of file diff --git a/include/argparse b/src/headers/argparse similarity index 81% rename from include/argparse rename to src/headers/argparse index 3270284..12059b2 100644 --- a/include/argparse +++ b/src/headers/argparse @@ -6,17 +6,17 @@ #ifdef __linux__ #include namespace argument_parser { - using parser = linux_parser; + using parser = linux_parser; } #elif __APPLE__ #include namespace argument_parser { - using parser = macos_parser; + using parser = macos_parser; } #elif _WIN32 #include namespace argument_parser { - using parser = windows_parser; + using parser = windows_parser; } #else #error "Unsupported platform" diff --git a/src/headers/conventions/base_convention.hpp b/src/headers/conventions/base_convention.hpp new file mode 100644 index 0000000..fc7d555 --- /dev/null +++ b/src/headers/conventions/base_convention.hpp @@ -0,0 +1,35 @@ +#pragma once +#include +#include + +#ifndef BASE_CONVENTION_HPP +#define BASE_CONVENTION_HPP + +namespace argument_parser::conventions { + enum class argument_type { SHORT, LONG, POSITIONAL, INTERCHANGABLE, ERROR }; + + using parsed_argument = std::pair; + + class base_convention { + public: + virtual std::string extract_value(std::string const &) const = 0; + virtual parsed_argument get_argument(std::string const &) const = 0; + virtual bool requires_next_token() const = 0; + virtual std::string name() const = 0; + virtual std::string short_prec() const = 0; + virtual std::string long_prec() const = 0; + + protected: + base_convention() = default; + ~base_convention() = default; + }; + using convention = base_convention; +} // namespace argument_parser::conventions + +namespace argument_parser::conventions::helpers { + static std::string to_lower(std::string s); + + static std::string to_upper(std::string s); +} // namespace argument_parser::conventions::helpers + +#endif \ No newline at end of file diff --git a/src/headers/conventions/implementations/gnu_argument_convention.hpp b/src/headers/conventions/implementations/gnu_argument_convention.hpp new file mode 100644 index 0000000..f48a133 --- /dev/null +++ b/src/headers/conventions/implementations/gnu_argument_convention.hpp @@ -0,0 +1,53 @@ +#pragma once +#include "base_convention.hpp" + +#ifndef GNU_ARGUMENT_CONVENTION_HPP +#define GNU_ARGUMENT_CONVENTION_HPP + +namespace argument_parser::conventions::implementations { + + class gnu_argument_convention : public base_convention { + public: + parsed_argument get_argument(std::string const &raw) const override; + std::string extract_value(std::string const & /*raw*/) const override; + + bool requires_next_token() const override; + + std::string name() const override; + + std::string short_prec() const override; + + std::string long_prec() const override; + + static gnu_argument_convention instance; + + private: + gnu_argument_convention() = default; + }; + + class gnu_equal_argument_convention : public base_convention { + public: + parsed_argument get_argument(std::string const &raw) const override; + std::string extract_value(std::string const &raw) const override; + bool requires_next_token() const override; + std::string name() const override; + std::string short_prec() const override; + std::string long_prec() const override; + static gnu_equal_argument_convention instance; + + private: + gnu_equal_argument_convention() = default; + }; + + inline gnu_argument_convention gnu_argument_convention::instance{}; + inline gnu_equal_argument_convention gnu_equal_argument_convention::instance{}; +} // namespace argument_parser::conventions::implementations + +namespace argument_parser::conventions { + static const implementations::gnu_argument_convention gnu_argument_convention = + implementations::gnu_argument_convention::instance; + static const implementations::gnu_equal_argument_convention gnu_equal_argument_convention = + implementations::gnu_equal_argument_convention::instance; +} // namespace argument_parser::conventions + +#endif \ No newline at end of file diff --git a/src/headers/conventions/implementations/windows_argument_convention.hpp b/src/headers/conventions/implementations/windows_argument_convention.hpp new file mode 100644 index 0000000..a8e7939 --- /dev/null +++ b/src/headers/conventions/implementations/windows_argument_convention.hpp @@ -0,0 +1,62 @@ +#pragma once +#include "base_convention.hpp" +#include + +#ifndef WINDOWS_ARGUMENT_CONVENTION_HPP +#define WINDOWS_ARGUMENT_CONVENTION_HPP + +#ifndef ALLOW_DASH_FOR_WINDOWS +#define ALLOW_DASH_FOR_WINDOWS 1 +#endif + +namespace argument_parser::conventions::implementations { + class windows_argument_convention : public base_convention { + public: + explicit windows_argument_convention(bool accept_dash = true); + + parsed_argument get_argument(std::string const &raw) const override; + + std::string extract_value(std::string const & /*raw*/) const override; + + bool requires_next_token() const override; + + std::string name() const override; + + std::string short_prec() const override; + + std::string long_prec() const override; + static windows_argument_convention instance; + + private: + bool accept_dash_; + }; + + class windows_kv_argument_convention : public base_convention { + public: + explicit windows_kv_argument_convention(bool accept_dash = true); + parsed_argument get_argument(std::string const &raw) const override; + std::string extract_value(std::string const &raw) const override; + bool requires_next_token() const override; + std::string name() const override; + std::string short_prec() const override; + std::string long_prec() const override; + static windows_kv_argument_convention instance; + + private: + bool accept_dash_; + }; + + inline windows_argument_convention windows_argument_convention::instance = + windows_argument_convention(bool(ALLOW_DASH_FOR_WINDOWS)); + inline windows_kv_argument_convention windows_kv_argument_convention::instance = + windows_kv_argument_convention(bool(ALLOW_DASH_FOR_WINDOWS)); +} // namespace argument_parser::conventions::implementations + +namespace argument_parser::conventions { + static inline const implementations::windows_argument_convention windows_argument_convention = + implementations::windows_argument_convention::instance; + static inline const implementations::windows_kv_argument_convention windows_equal_argument_convention = + implementations::windows_kv_argument_convention::instance; +} // namespace argument_parser::conventions + +#endif // WINDOWS_ARGUMENT_CONVENTION_HPP \ No newline at end of file diff --git a/include/parser/argument_parser.hpp b/src/headers/parser/argument_parser.hpp similarity index 100% rename from include/parser/argument_parser.hpp rename to src/headers/parser/argument_parser.hpp diff --git a/include/parser/fake_parser.hpp b/src/headers/parser/fake_parser.hpp similarity index 100% rename from include/parser/fake_parser.hpp rename to src/headers/parser/fake_parser.hpp diff --git a/include/parser/parser_v2.hpp b/src/headers/parser/parser_v2.hpp similarity index 100% rename from include/parser/parser_v2.hpp rename to src/headers/parser/parser_v2.hpp diff --git a/include/parser/parsing_traits/traits.hpp b/src/headers/parser/parsing_traits/traits.hpp similarity index 100% rename from include/parser/parsing_traits/traits.hpp rename to src/headers/parser/parsing_traits/traits.hpp diff --git a/include/parser/platform_headers/linux_parser.hpp b/src/headers/parser/platform_headers/linux_parser.hpp similarity index 100% rename from include/parser/platform_headers/linux_parser.hpp rename to src/headers/parser/platform_headers/linux_parser.hpp diff --git a/include/parser/platform_headers/macos_parser.hpp b/src/headers/parser/platform_headers/macos_parser.hpp similarity index 100% rename from include/parser/platform_headers/macos_parser.hpp rename to src/headers/parser/platform_headers/macos_parser.hpp diff --git a/include/parser/platform_headers/windows_parser.hpp b/src/headers/parser/platform_headers/windows_parser.hpp similarity index 100% rename from include/parser/platform_headers/windows_parser.hpp rename to src/headers/parser/platform_headers/windows_parser.hpp diff --git a/src/source/conventions/base_convention.cpp b/src/source/conventions/base_convention.cpp new file mode 100644 index 0000000..debbfda --- /dev/null +++ b/src/source/conventions/base_convention.cpp @@ -0,0 +1,14 @@ +#include "base_convention.hpp" +#include + +namespace argument_parser::conventions::helpers { + static std::string to_lower(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); + return s; + } + + static std::string to_upper(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::toupper(c); }); + return s; + } +} // namespace argument_parser::conventions::helpers \ No newline at end of file diff --git a/src/source/conventions/implementations/gnu_argument_convention.cpp b/src/source/conventions/implementations/gnu_argument_convention.cpp new file mode 100644 index 0000000..85baa88 --- /dev/null +++ b/src/source/conventions/implementations/gnu_argument_convention.cpp @@ -0,0 +1,75 @@ +#include "gnu_argument_convention.hpp" +#include "base_convention.hpp" +#include + +bool starts_with(std::string const &s, std::string const &prefix) { + return s.rfind(prefix, 0) == 0; +} + +namespace argument_parser::conventions::implementations { + parsed_argument gnu_argument_convention::get_argument(std::string const &raw) const { + if (starts_with(raw, long_prec())) + return {argument_type::LONG, raw.substr(2)}; + else if (starts_with(raw, short_prec())) + return {argument_type::SHORT, raw.substr(1)}; + else + return {argument_type::ERROR, "GNU standard convention does not allow arguments without a preceding dash."}; + } + + std::string gnu_argument_convention::extract_value(std::string const & /*raw*/) const { + throw std::runtime_error("No inline value in standard GNU convention."); + } + + bool gnu_argument_convention::requires_next_token() const { + return true; + } + + std::string gnu_argument_convention::name() const { + return "GNU-style long options"; + } + + std::string gnu_argument_convention::short_prec() const { + return "-"; + } + + std::string gnu_argument_convention::long_prec() const { + return "--"; + } +} // namespace argument_parser::conventions::implementations + +namespace argument_parser::conventions::implementations { + parsed_argument gnu_equal_argument_convention::get_argument(std::string const &raw) const { + auto pos = raw.find('='); + auto arg = pos != std::string::npos ? raw.substr(0, pos) : raw; + if (starts_with(arg, long_prec())) + return {argument_type::LONG, arg.substr(2)}; + else if (starts_with(arg, short_prec())) + return {argument_type::SHORT, arg.substr(1)}; + else + return {argument_type::ERROR, "GNU standard convention does not allow arguments without a preceding dash."}; + } + + std::string gnu_equal_argument_convention::extract_value(std::string const &raw) const { + 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 gnu_equal_argument_convention::requires_next_token() const { + return false; + } + + std::string gnu_equal_argument_convention::name() const { + return "GNU-style long options (equal signed form)"; + } + + std::string gnu_equal_argument_convention::short_prec() const { + return "-"; + } + + std::string gnu_equal_argument_convention::long_prec() const { + return "--"; + } + +} // namespace argument_parser::conventions::implementations \ No newline at end of file diff --git a/src/source/conventions/implementations/windows_argument_convention.cpp b/src/source/conventions/implementations/windows_argument_convention.cpp new file mode 100644 index 0000000..8f2a280 --- /dev/null +++ b/src/source/conventions/implementations/windows_argument_convention.cpp @@ -0,0 +1,103 @@ +#include "windows_argument_convention.hpp" + +namespace argument_parser::conventions::implementations { + windows_argument_convention::windows_argument_convention(bool accept_dash) : accept_dash_(accept_dash) {} + + parsed_argument windows_argument_convention::get_argument(std::string const &raw) const { + if (raw.empty()) { + return {argument_type::ERROR, "Empty argument token."}; + } + const char c0 = raw[0]; + const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-'); + if (!ok_prefix) { + return {argument_type::ERROR, + accept_dash_ ? "Windows-style expects options to start with '/' (or '-' in compat mode)." + : "Windows-style expects options to start with '/'."}; + } + + if (raw.find_first_of("=:") != std::string::npos) { + return {argument_type::ERROR, + "Inline values are not allowed in this convention; provide the value in the next token."}; + } + + std::string name = helpers::to_lower(raw.substr(1)); + if (name.empty()) { + return {argument_type::ERROR, "Option name cannot be empty after '/'."}; + } + + return {argument_type::INTERCHANGABLE, std::move(name)}; + } + + std::string windows_argument_convention::extract_value(std::string const & /*raw*/) const { + throw std::runtime_error("No inline value; value must be provided in the next token."); + } + + bool windows_argument_convention::requires_next_token() const { + return true; + } + + std::string windows_argument_convention::name() const { + return "Windows style options (next-token values)"; + } + + std::string windows_argument_convention::short_prec() const { + return accept_dash_ ? "-" : "/"; + } + + std::string windows_argument_convention::long_prec() const { + return "/"; + } + +} // namespace argument_parser::conventions::implementations + +namespace argument_parser::conventions::implementations { + windows_kv_argument_convention::windows_kv_argument_convention(bool accept_dash) : accept_dash_(accept_dash) {} + + parsed_argument windows_kv_argument_convention::get_argument(std::string const &raw) const { + if (raw.empty()) { + return {argument_type::ERROR, "Empty argument token."}; + } + const char c0 = raw[0]; + const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-'); + if (!ok_prefix) { + return {argument_type::ERROR, + accept_dash_ ? "Windows-style expects options to start with '/' (or '-' in compat mode)." + : "Windows-style expects options to start with '/'."}; + } + + const std::size_t sep = raw.find_first_of("=:"); + if (sep == std::string::npos) { + return {argument_type::ERROR, + "Expected an inline value using '=' or ':' (e.g., /opt=value or /opt:value)."}; + } + if (sep == 1) { + return {argument_type::ERROR, "Option name cannot be empty before '=' or ':'."}; + } + + std::string name = helpers::to_lower(raw.substr(1, sep - 1)); + return {argument_type::INTERCHANGABLE, std::move(name)}; + } + + std::string windows_kv_argument_convention::extract_value(std::string const &raw) const { + const std::size_t sep = raw.find_first_of("=:"); + if (sep == std::string::npos || sep + 1 >= raw.size()) + throw std::runtime_error("Expected a value after '=' or ':'."); + return raw.substr(sep + 1); + } + + bool windows_kv_argument_convention::requires_next_token() const { + return false; + } + + std::string windows_kv_argument_convention::name() const { + return "Windows-style options (inline values via '=' or ':')"; + } + + std::string windows_kv_argument_convention::short_prec() const { + return accept_dash_ ? "-" : "/"; + } + + std::string windows_kv_argument_convention::long_prec() const { + return "/"; + } +} // namespace argument_parser::conventions::implementations \ No newline at end of file