update(conventions): extend windows conventions to show more capability.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#ifndef BASE_CONVENTION_HPP
|
#ifndef BASE_CONVENTION_HPP
|
||||||
@@ -31,4 +32,18 @@ namespace argument_parser::conventions {
|
|||||||
using convention = base_convention;
|
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
|
#endif
|
||||||
@@ -5,87 +5,116 @@
|
|||||||
#ifndef WINDOWS_ARGUMENT_CONVENTION_HPP
|
#ifndef WINDOWS_ARGUMENT_CONVENTION_HPP
|
||||||
#define 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 {
|
namespace argument_parser::conventions::implementations {
|
||||||
class windows_argument_convention : public base_convention {
|
class windows_argument_convention : public base_convention {
|
||||||
public:
|
public:
|
||||||
// Windows style options are prefixed with a slash ('/'). And there's no difference for short vs long options.
|
explicit windows_argument_convention(bool accept_dash = true)
|
||||||
|
: accept_dash_(accept_dash) {
|
||||||
|
}
|
||||||
|
|
||||||
parsed_argument get_argument(std::string const& raw) const override {
|
parsed_argument get_argument(std::string const& raw) const override {
|
||||||
if (raw.starts_with(long_prec()))
|
if (raw.empty()) {
|
||||||
return { argument_type::INTERCHANGABLE, raw.substr(1) };
|
return { argument_type::ERROR, "Empty argument token." };
|
||||||
else
|
}
|
||||||
return { argument_type::ERROR, "Windows standard requires non-positional arguments with a preceeding slash ('\\')." };
|
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 {
|
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; value must be provided in the next token.");
|
||||||
throw std::runtime_error("No inline value in standard GNU convention.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool requires_next_token() const override {
|
bool requires_next_token() const override { return true; }
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name() const override {
|
std::string name() const override { return "Windows style options (next-token values)"; }
|
||||||
return "Windows style options";
|
std::string short_prec() const override { return accept_dash_ ? "-" : "/"; }
|
||||||
}
|
std::string long_prec() const override { return "/"; }
|
||||||
|
|
||||||
std::string short_prec() const override {
|
static windows_argument_convention instance;
|
||||||
return "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string long_prec() const override {
|
|
||||||
return "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
static windows_argument_convention instance;
|
|
||||||
private:
|
private:
|
||||||
windows_argument_convention() = default;
|
bool accept_dash_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class windows_equal_argument_convention : public base_convention {
|
class windows_kv_argument_convention : public base_convention {
|
||||||
public:
|
public:
|
||||||
// Windows style options are prefixed with a slash ('/'). And there's no difference for short vs long options.
|
explicit windows_kv_argument_convention(bool accept_dash = true)
|
||||||
|
: accept_dash_(accept_dash) {
|
||||||
|
}
|
||||||
|
|
||||||
parsed_argument get_argument(std::string const& raw) const override {
|
parsed_argument get_argument(std::string const& raw) const override {
|
||||||
auto pos = raw.find('=');
|
if (raw.empty()) {
|
||||||
auto arg = pos != std::string::npos ? raw.substr(0, pos) : raw;
|
return { argument_type::ERROR, "Empty argument token." };
|
||||||
if (arg.starts_with(long_prec()))
|
}
|
||||||
return { argument_type::INTERCHANGABLE, arg.substr(1) };
|
const char c0 = raw[0];
|
||||||
else
|
const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-');
|
||||||
return { argument_type::ERROR, "Windows standard requires non-positional arguments with a preceeding slash ('\\')." };
|
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 {
|
std::string extract_value(std::string const& raw) const override {
|
||||||
auto pos = raw.find('=');
|
const std::size_t sep = raw.find_first_of("=:");
|
||||||
if (pos == std::string::npos || pos + 1 >= raw.size())
|
if (sep == std::string::npos || sep + 1 >= raw.size())
|
||||||
throw std::runtime_error("Expected value after '='.");
|
throw std::runtime_error("Expected a value after '=' or ':'.");
|
||||||
return raw.substr(pos + 1);
|
return raw.substr(sep + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool requires_next_token() const override {
|
bool requires_next_token() const override { return false; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name() const override {
|
std::string name() const override { return "Windows style options (inline values via '=' or ':')"; }
|
||||||
return "Windows style options (equal signed form)";
|
std::string short_prec() const override { return accept_dash_ ? "-" : "/"; }
|
||||||
}
|
std::string long_prec() const override { return "/"; }
|
||||||
|
|
||||||
std::string short_prec() const override {
|
static windows_kv_argument_convention instance;
|
||||||
return "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string long_prec() const override {
|
|
||||||
return "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
static windows_equal_argument_convention instance;
|
|
||||||
private:
|
private:
|
||||||
windows_equal_argument_convention() = default;
|
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 {
|
namespace argument_parser::conventions {
|
||||||
static inline const implementations::windows_argument_convention windows_argument_convention = implementations::windows_argument_convention::instance;
|
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;
|
static inline const implementations::windows_kv_argument_convention windows_equal_argument_convention = implementations::windows_kv_argument_convention::instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // WINDOWS_ARGUMENT_CONVENTION_HPP
|
#endif // WINDOWS_ARGUMENT_CONVENTION_HPP
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#define ALLOW_DASH_FOR_WINDOWS 0
|
||||||
|
|
||||||
#include <argparse>
|
#include <argparse>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|||||||
Reference in New Issue
Block a user