From 776adb0c9678afa7b9b37d0d92537d7781de29d6 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Tue, 23 Sep 2025 16:13:52 +0800 Subject: [PATCH] feat: finish option and variable in clap --- src/CMakeLists.txt | 4 +- src/yycc/carton/clap/option.cpp | 176 +++++++++++++++++++++++++++++- src/yycc/carton/clap/option.hpp | 102 +++++++++-------- src/yycc/carton/clap/types.hpp | 15 +++ src/yycc/carton/clap/variable.cpp | 92 ++++++++++++++++ src/yycc/carton/clap/variable.hpp | 65 +++++++++++ 6 files changed, 398 insertions(+), 56 deletions(-) create mode 100644 src/yycc/carton/clap/variable.cpp create mode 100644 src/yycc/carton/clap/variable.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e032a90..cdbf96e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/yycc/carton/clap/option.cpp b/src/yycc/carton/clap/option.cpp index 0f6f72e..f1c56d3 100644 --- a/src/yycc/carton/clap/option.cpp +++ b/src/yycc/carton/clap/option.cpp @@ -1,5 +1,177 @@ #include "option.hpp" +#include "../../string/op.hpp" +#include +#include + +#define TYPES ::yycc::carton::clap::types +#define OP ::yycc::string::op namespace yycc::carton::clap::option { - -} + +#pragma region Option + + Option::Option(std::optional short_name, + std::optional long_name, + std::optional 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 Option::get_short_name() const { + return this->short_name; + } + + std::optional Option::get_long_name() const { + return this->long_name; + } + + std::optional 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 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 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& 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 diff --git a/src/yycc/carton/clap/option.hpp b/src/yycc/carton/clap/option.hpp index bfbde59..d786d7e 100644 --- a/src/yycc/carton/clap/option.hpp +++ b/src/yycc/carton/clap/option.hpp @@ -1,11 +1,10 @@ #pragma once #include "types.hpp" #include "../../macro/class_copy_move.hpp" -#include "../../string/op.hpp" #include -#include -#include #include +#include +#include #define NS_YYCC_CLAP_TYPES ::yycc::carton::clap::types @@ -16,62 +15,22 @@ namespace yycc::carton::clap::option { Option(std::optional short_name, std::optional long_name, std::optional 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 get_short_name() const { return this->short_name; } - std::optional get_long_name() const { return this->long_name; } - std::optional get_value_hint() const { return this->value_hint; } - std::u8string_view get_description() const { return this->description; } + std::optional get_short_name() const; + std::optional get_long_name() const; + std::optional 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 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 find_long_name(const std::u8string_view& long_name) const; + std::optional 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& all_options() const; + size_t length() const; + bool empty() const; + + private: + std::map short_names; + std::map long_names; + std::vector options; + }; + } // namespace yycc::carton::clap::option #undef NS_YYCC_CLAP_TYPES diff --git a/src/yycc/carton/clap/types.hpp b/src/yycc/carton/clap/types.hpp index 0f790d5..0d6f1b8 100644 --- a/src/yycc/carton/clap/types.hpp +++ b/src/yycc/carton/clap/types.hpp @@ -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 using ClapResult = std::expected; + /// @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 diff --git a/src/yycc/carton/clap/variable.cpp b/src/yycc/carton/clap/variable.cpp new file mode 100644 index 0000000..6321788 --- /dev/null +++ b/src/yycc/carton/clap/variable.cpp @@ -0,0 +1,92 @@ +#include "variable.hpp" +#include +#include + +#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 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 &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 diff --git a/src/yycc/carton/clap/variable.hpp b/src/yycc/carton/clap/variable.hpp new file mode 100644 index 0000000..e3a0eb4 --- /dev/null +++ b/src/yycc/carton/clap/variable.hpp @@ -0,0 +1,65 @@ +#pragma once +#include "types.hpp" +#include "../../macro/class_copy_move.hpp" +#include +#include +#include +#include + +#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 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& all_variables() const; + size_t length() const; + bool empty() const; + + private: + std::map names; + std::vector variables; + }; + +} // namespace yycc::carton::clap::variable + +#undef NS_YYCC_CLAP_TYPES