mirror of
https://github.com/sametersoylu/argument-parser.git
synced 2026-04-13 03:41:18 +00:00
feat: Introduce a modular argument convention system for GNU and Windows styles, add .clang-format, and update CMake configuration to C++17.
This commit is contained in:
14
src/source/conventions/base_convention.cpp
Normal file
14
src/source/conventions/base_convention.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "base_convention.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
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
|
||||
@@ -0,0 +1,75 @@
|
||||
#include "gnu_argument_convention.hpp"
|
||||
#include "base_convention.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user