feat: finish option and variable in clap
This commit is contained in:
@ -31,7 +31,8 @@ PRIVATE
|
||||
yycc/carton/tabulate.cpp
|
||||
yycc/carton/ironpad.cpp
|
||||
yycc/carton/csconsole.cpp
|
||||
#yycc/carton/clap/option.cpp
|
||||
yycc/carton/clap/option.cpp
|
||||
yycc/carton/clap/variable.cpp
|
||||
)
|
||||
target_sources(YYCCommonplace
|
||||
PUBLIC
|
||||
@ -86,6 +87,7 @@ FILES
|
||||
yycc/carton/clap.hpp
|
||||
yycc/carton/clap/types.hpp
|
||||
yycc/carton/clap/option.hpp
|
||||
yycc/carton/clap/variable.hpp
|
||||
)
|
||||
# Setup header infomations
|
||||
target_include_directories(YYCCommonplace
|
||||
|
@ -1,5 +1,177 @@
|
||||
#include "option.hpp"
|
||||
#include "../../string/op.hpp"
|
||||
#include <stdexcept>
|
||||
#include <format>
|
||||
|
||||
#define TYPES ::yycc::carton::clap::types
|
||||
#define OP ::yycc::string::op
|
||||
|
||||
namespace yycc::carton::clap::option {
|
||||
|
||||
#pragma region Option
|
||||
|
||||
Option::Option(std::optional<std::u8string_view> short_name,
|
||||
std::optional<std::u8string_view> long_name,
|
||||
std::optional<std::u8string_view> value_hint,
|
||||
const std::u8string& description) :
|
||||
short_name(short_name), long_name(long_name), value_hint(value_hint), description(description) {
|
||||
if (!short_name.has_value() && !long_name.has_value()) {
|
||||
throw std::logic_error("must have at least one name, short or long name");
|
||||
}
|
||||
|
||||
if (short_name.has_value()) {
|
||||
const auto& short_name_value = short_name.value();
|
||||
if (!legal_short_name(short_name_value)) {
|
||||
throw std::logic_error(std::format("invalid short name {}", short_name_value));
|
||||
}
|
||||
}
|
||||
if (long_name.has_value()) {
|
||||
const auto& long_name_value = long_name.value();
|
||||
if (!legal_long_name(long_name_value)) {
|
||||
throw std::logic_error(std::format("invalid long name {}", long_name_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Option::~Option() {}
|
||||
|
||||
std::optional<std::u8string_view> Option::get_short_name() const {
|
||||
return this->short_name;
|
||||
}
|
||||
|
||||
std::optional<std::u8string_view> Option::get_long_name() const {
|
||||
return this->long_name;
|
||||
}
|
||||
|
||||
std::optional<std::u8string_view> Option::get_value_hint() const {
|
||||
return this->value_hint;
|
||||
}
|
||||
|
||||
std::u8string_view Option::get_description() const {
|
||||
return this->description;
|
||||
}
|
||||
|
||||
std::u8string Option::to_showcase_name() const {
|
||||
if (short_name.has_value()) {
|
||||
if (long_name.has_value()) {
|
||||
return OP::printf(u8"%s%s %s%s", TYPES::DASH, short_name.value().c_str(), TYPES::DOUBLE_DASH, long_name.value().c_str());
|
||||
} else {
|
||||
return OP::printf(u8"%s%s", TYPES::DASH, short_name.value().c_str());
|
||||
}
|
||||
} else {
|
||||
if (long_name.has_value()) {
|
||||
return OP::printf(u8"%s%s", TYPES::DOUBLE_DASH, long_name.value().c_str());
|
||||
} else {
|
||||
throw std::runtime_error("both long name and short name are empty");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::u8string Option::to_showcase_value() const {
|
||||
if (value_hint.has_value()) {
|
||||
return OP::printf(u8"<%s>", value_hint.value().c_str());
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
bool Option::legal_short_name(const std::u8string_view& name) {
|
||||
if (name.empty()) return false;
|
||||
if (name.starts_with(TYPES::DASH)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Option::legal_long_name(const std::u8string_view& name) {
|
||||
if (name.empty()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Registered Option
|
||||
|
||||
RegisteredOption::RegisteredOption(TYPES::Token token, Option&& option) : token(token), option(std::move(option)) {}
|
||||
|
||||
RegisteredOption::~RegisteredOption() {}
|
||||
|
||||
TYPES::Token RegisteredOption::get_token() const {
|
||||
return this->token;
|
||||
}
|
||||
|
||||
const Option& RegisteredOption::get_option() const {
|
||||
return this->option;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Option Collection
|
||||
|
||||
OptionCollection::OptionCollection() : short_names(), long_names(), options() {}
|
||||
|
||||
OptionCollection::~OptionCollection() {}
|
||||
|
||||
TYPES::Token OptionCollection::add_option(Option&& opt) {
|
||||
auto token = this->options.size();
|
||||
|
||||
const auto& short_name = opt.get_short_name();
|
||||
if (short_name.has_value()) {
|
||||
std::u8string short_name_value(short_name.value());
|
||||
if (this->long_names.contains(short_name_value)) {
|
||||
throw std::logic_error(std::format("short name {} is duplicated with same long name", short_name_value));
|
||||
}
|
||||
auto [_, ok] = this->short_names.try_emplace(short_name_value, token);
|
||||
if (!ok) {
|
||||
throw std::logic_error(std::format("duplicate short name {}", short_name_value));
|
||||
}
|
||||
}
|
||||
const auto& long_name = opt.get_long_name();
|
||||
if (long_name.has_value()) {
|
||||
std::u8string long_name_value(long_name.value());
|
||||
if (this->short_names.contains(long_name_value)) {
|
||||
throw std::logic_error(std::format("long name {} is duplicated with same short name", long_name_value));
|
||||
}
|
||||
auto [_, ok] = this->long_names.try_emplace(long_name_value, token);
|
||||
if (!ok) {
|
||||
throw std::logic_error(std::format("duplicate long name {}", long_name_value));
|
||||
}
|
||||
}
|
||||
|
||||
this->options.emplace_back(RegisteredOption(token, std::move(opt)));
|
||||
return token;
|
||||
}
|
||||
|
||||
std::optional<TYPES::Token> OptionCollection::find_long_name(const std::u8string_view& long_name) const {
|
||||
auto finder = this->long_names.find(std::u8string(long_name));
|
||||
if (finder == this->long_names.end()) return std::nullopt;
|
||||
else return finder->second;
|
||||
}
|
||||
|
||||
std::optional<TYPES::Token> OptionCollection::find_short_name(const std::u8string_view& short_name) const {
|
||||
auto finder = this->short_names.find(std::u8string(short_name));
|
||||
if (finder == this->short_names.end()) return std::nullopt;
|
||||
else return finder->second;
|
||||
}
|
||||
|
||||
bool OptionCollection::has_option(TYPES::Token token) const {
|
||||
return token < this->options.size();
|
||||
}
|
||||
|
||||
const Option& OptionCollection::get_option(TYPES::Token token) const {
|
||||
return this->options.at(token).get_option();
|
||||
}
|
||||
|
||||
const std::vector<RegisteredOption>& OptionCollection::all_options() const {
|
||||
return this->options;
|
||||
}
|
||||
|
||||
size_t OptionCollection::length() const {
|
||||
return this->options.size();
|
||||
}
|
||||
|
||||
bool OptionCollection::empty() const {
|
||||
return this->options.empty();
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace yycc::carton::clap::option
|
||||
|
@ -1,11 +1,10 @@
|
||||
#pragma once
|
||||
#include "types.hpp"
|
||||
#include "../../macro/class_copy_move.hpp"
|
||||
#include "../../string/op.hpp"
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <format>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#define NS_YYCC_CLAP_TYPES ::yycc::carton::clap::types
|
||||
|
||||
@ -16,62 +15,22 @@ namespace yycc::carton::clap::option {
|
||||
Option(std::optional<std::u8string_view> short_name,
|
||||
std::optional<std::u8string_view> long_name,
|
||||
std::optional<std::u8string_view> value_hint,
|
||||
const std::u8string& description) :
|
||||
short_name(short_name), long_name(long_name), value_hint(value_hint), description(description) {
|
||||
if (!short_name.has_value() && !long_name.has_value()) {
|
||||
throw std::logic_error("must have at least one name, short or long name");
|
||||
}
|
||||
|
||||
if (short_name.has_value()) {
|
||||
const auto& short_name_value = short_name.value();
|
||||
if (!legal_short_name(short_name_value)) {
|
||||
throw std::logic_error(std::format("invalid short name {}", short_name_value));
|
||||
}
|
||||
}
|
||||
if (long_name.has_value()) {
|
||||
const auto& long_name_value = long_name.value();
|
||||
if (!legal_long_name(long_name_value)) {
|
||||
throw std::logic_error(std::format("invalid long name {}", long_name_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
~Option() {}
|
||||
const std::u8string& description);
|
||||
~Option();
|
||||
YYCC_DEFAULT_COPY_MOVE(Option)
|
||||
|
||||
public:
|
||||
std::optional<std::u8string_view> get_short_name() const { return this->short_name; }
|
||||
std::optional<std::u8string_view> get_long_name() const { return this->long_name; }
|
||||
std::optional<std::u8string_view> get_value_hint() const { return this->value_hint; }
|
||||
std::u8string_view get_description() const { return this->description; }
|
||||
std::optional<std::u8string_view> get_short_name() const;
|
||||
std::optional<std::u8string_view> get_long_name() const;
|
||||
std::optional<std::u8string_view> get_value_hint() const;
|
||||
std::u8string_view get_description() const;
|
||||
|
||||
std::u8string to_showcase_name() {
|
||||
namespace op = ::yycc::string::op;
|
||||
|
||||
if (short_name.has_value()) {
|
||||
if (long_name.has_value()) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
} else {
|
||||
if (long_name.has_value()) {
|
||||
op::printf
|
||||
} else {
|
||||
throw std::runtime_error("both long name and short name are empty");
|
||||
}
|
||||
}
|
||||
}
|
||||
std::u8string to_showcase_name() const;
|
||||
std::u8string to_showcase_value() const;
|
||||
|
||||
private:
|
||||
static bool legal_short_name(const std::u8string_view& name) {
|
||||
if (name.empty()) return false;
|
||||
if (name.starts_with(NS_YYCC_CLAP_TYPES::DASH)) return false;
|
||||
return true;
|
||||
}
|
||||
static bool legal_long_name(const std::u8string_view& name) {
|
||||
if (name.empty()) return false;
|
||||
return true;
|
||||
}
|
||||
static bool legal_short_name(const std::u8string_view& name);
|
||||
static bool legal_long_name(const std::u8string_view& name);
|
||||
|
||||
private:
|
||||
std::optional<std::u8string> short_name;
|
||||
@ -80,6 +39,43 @@ namespace yycc::carton::clap::option {
|
||||
std::u8string description;
|
||||
};
|
||||
|
||||
class RegisteredOption {
|
||||
public:
|
||||
RegisteredOption(NS_YYCC_CLAP_TYPES::Token token, Option&& option);
|
||||
~RegisteredOption();
|
||||
YYCC_DEFAULT_COPY_MOVE(RegisteredOption)
|
||||
|
||||
public:
|
||||
NS_YYCC_CLAP_TYPES::Token get_token() const;
|
||||
const Option& get_option() const;
|
||||
|
||||
private:
|
||||
NS_YYCC_CLAP_TYPES::Token token;
|
||||
Option option;
|
||||
};
|
||||
|
||||
class OptionCollection {
|
||||
public:
|
||||
OptionCollection();
|
||||
~OptionCollection();
|
||||
YYCC_DEFAULT_COPY_MOVE(OptionCollection)
|
||||
|
||||
public:
|
||||
NS_YYCC_CLAP_TYPES::Token add_option(Option&& opt);
|
||||
std::optional<NS_YYCC_CLAP_TYPES::Token> find_long_name(const std::u8string_view& long_name) const;
|
||||
std::optional<NS_YYCC_CLAP_TYPES::Token> find_short_name(const std::u8string_view& short_name) const;
|
||||
bool has_option(NS_YYCC_CLAP_TYPES::Token token) const;
|
||||
const Option& get_option(NS_YYCC_CLAP_TYPES::Token token) const;
|
||||
const std::vector<RegisteredOption>& all_options() const;
|
||||
size_t length() const;
|
||||
bool empty() const;
|
||||
|
||||
private:
|
||||
std::map<std::u8string, NS_YYCC_CLAP_TYPES::Token> short_names;
|
||||
std::map<std::u8string, NS_YYCC_CLAP_TYPES::Token> long_names;
|
||||
std::vector<RegisteredOption> options;
|
||||
};
|
||||
|
||||
} // namespace yycc::carton::clap::option
|
||||
|
||||
#undef NS_YYCC_CLAP_TYPES
|
||||
|
@ -4,16 +4,31 @@
|
||||
|
||||
namespace yycc::carton::clap::types {
|
||||
|
||||
/// @brief All possible error kind occurs in this module.
|
||||
enum class ClapError {
|
||||
|
||||
};
|
||||
|
||||
/// @brief The result type used in this module.
|
||||
template<typename T>
|
||||
using ClapResult = std::expected<T, ClapError>;
|
||||
|
||||
/// @brief The dash prefix used for short name of option.
|
||||
inline constexpr std::u8string_view DASH = u8"-";
|
||||
/// @brief The double dash prefix used by long name of option.
|
||||
inline constexpr std::u8string_view DOUBLE_DASH = u8"--";
|
||||
|
||||
/**
|
||||
* @brief An unique token type.
|
||||
* @details
|
||||
* When outside code registering an option or variable,
|
||||
* there must be a token returned by manager.
|
||||
* When outside code want to visit this registered item again,
|
||||
* they should provide this token returned when registering.
|
||||
*
|
||||
* Its value actually is the index of its stored vector.
|
||||
* So this type is an alias to vector size type.
|
||||
*/
|
||||
using Token = size_t;
|
||||
|
||||
} // namespace yycc::carton::clap::types
|
||||
|
92
src/yycc/carton/clap/variable.cpp
Normal file
92
src/yycc/carton/clap/variable.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
#include "variable.hpp"
|
||||
#include <stdexcept>
|
||||
#include <format>
|
||||
|
||||
#define TYPES ::yycc::carton::clap::types
|
||||
|
||||
namespace yycc::carton::clap::variable {
|
||||
|
||||
#pragma region Variable
|
||||
|
||||
Variable::Variable(const std::u8string_view &name, const std::u8string_view &description) : name(name), description(description) {
|
||||
if (name.empty()) {
|
||||
throw std::logic_error("the name of variable should not be empty");
|
||||
}
|
||||
}
|
||||
|
||||
Variable::~Variable() {}
|
||||
|
||||
std::u8string_view Variable::get_name() const {
|
||||
return this->name;
|
||||
}
|
||||
|
||||
std::u8string_view Variable::get_description() const {
|
||||
return this->description;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Registered Variable
|
||||
|
||||
RegisteredVariable::RegisteredVariable(TYPES::Token token, Variable &&variable) : token(token), variable(std::move(variable)) {}
|
||||
|
||||
RegisteredVariable::~RegisteredVariable() {}
|
||||
|
||||
TYPES::Token RegisteredVariable::get_token() const {
|
||||
return this->token;
|
||||
}
|
||||
|
||||
const Variable &RegisteredVariable::get_variable() const {
|
||||
return this->variable;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Variable Collection
|
||||
|
||||
VariableCollection::VariableCollection() : names(), variables() {}
|
||||
|
||||
VariableCollection::~VariableCollection() {}
|
||||
|
||||
TYPES::Token VariableCollection::add_variable(Variable &&var) {
|
||||
auto token = this->variables.size();
|
||||
|
||||
std::u8string name(var.get_name());
|
||||
auto [_, ok] = this->names.try_emplace(name, token);
|
||||
if (!ok) {
|
||||
throw std::logic_error(std::format("duplicated variable name {}", name));
|
||||
}
|
||||
|
||||
this->variables.emplace_back(RegisteredVariable(token, std::move(var)));
|
||||
return token;
|
||||
}
|
||||
|
||||
std::optional<TYPES::Token> VariableCollection::find_name(const std::u8string_view &name) const {
|
||||
auto finder = this->names.find(std::u8string(name));
|
||||
if (finder == this->names.end()) return std::nullopt;
|
||||
else return finder->second;
|
||||
}
|
||||
|
||||
bool VariableCollection::has_variable(TYPES::Token token) const {
|
||||
return token < this->variables.size();
|
||||
}
|
||||
|
||||
const Variable &VariableCollection::get_variable(TYPES::Token token) const {
|
||||
return this->variables.at(token).get_variable();
|
||||
}
|
||||
|
||||
const std::vector<RegisteredVariable> &VariableCollection::all_variables() const {
|
||||
return this->variables;
|
||||
}
|
||||
|
||||
size_t VariableCollection::length() const {
|
||||
return this->variables.size();
|
||||
}
|
||||
|
||||
bool VariableCollection::empty() const {
|
||||
return this->variables.empty();
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace yycc::carton::clap::variable
|
65
src/yycc/carton/clap/variable.hpp
Normal file
65
src/yycc/carton/clap/variable.hpp
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include "types.hpp"
|
||||
#include "../../macro/class_copy_move.hpp"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#define NS_YYCC_CLAP_TYPES ::yycc::carton::clap::types
|
||||
|
||||
namespace yycc::carton::clap::variable {
|
||||
|
||||
class Variable {
|
||||
public:
|
||||
Variable(const std::u8string_view& name, const std::u8string_view& description);
|
||||
~Variable();
|
||||
YYCC_DEFAULT_COPY_MOVE(Variable)
|
||||
|
||||
public:
|
||||
std::u8string_view get_name() const;
|
||||
std::u8string_view get_description() const;
|
||||
|
||||
private:
|
||||
std::u8string name;
|
||||
std::u8string description;
|
||||
};
|
||||
|
||||
class RegisteredVariable {
|
||||
public:
|
||||
RegisteredVariable(NS_YYCC_CLAP_TYPES::Token token, Variable&& variable);
|
||||
~RegisteredVariable();
|
||||
YYCC_DEFAULT_COPY_MOVE(RegisteredVariable)
|
||||
|
||||
public:
|
||||
NS_YYCC_CLAP_TYPES::Token get_token() const;
|
||||
const Variable& get_variable() const;
|
||||
|
||||
private:
|
||||
NS_YYCC_CLAP_TYPES::Token token;
|
||||
Variable variable;
|
||||
};
|
||||
|
||||
class VariableCollection {
|
||||
public:
|
||||
VariableCollection();
|
||||
~VariableCollection();
|
||||
YYCC_DEFAULT_COPY_MOVE(VariableCollection)
|
||||
|
||||
public:
|
||||
NS_YYCC_CLAP_TYPES::Token add_variable(Variable&& var);
|
||||
std::optional<NS_YYCC_CLAP_TYPES::Token> find_name(const std::u8string_view& name) const;
|
||||
bool has_variable(NS_YYCC_CLAP_TYPES::Token token) const;
|
||||
const Variable& get_variable(NS_YYCC_CLAP_TYPES::Token token) const;
|
||||
const std::vector<RegisteredVariable>& all_variables() const;
|
||||
size_t length() const;
|
||||
bool empty() const;
|
||||
|
||||
private:
|
||||
std::map<std::u8string, NS_YYCC_CLAP_TYPES::Token> names;
|
||||
std::vector<RegisteredVariable> variables;
|
||||
};
|
||||
|
||||
} // namespace yycc::carton::clap::variable
|
||||
|
||||
#undef NS_YYCC_CLAP_TYPES
|
Reference in New Issue
Block a user