From 5fe8cc34e6807ba70e7021fd4d29d503b28f8b23 Mon Sep 17 00:00:00 2001 From: yizhi <946185759@qq.com> Date: Mon, 24 Feb 2025 09:47:45 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AC=A1=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + CMakeLists.txt | 9 + src/commander.cc | 640 +++++++++++++++++++++++++++++++++++++++++++++++ src/commander.h | 117 +++++++++ src/main.cc | 33 +++ 5 files changed, 802 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 src/commander.cc create mode 100644 src/commander.h create mode 100644 src/main.cc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0c67607 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.vscode +/build +/install \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0246f66 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.10.0) +project(commander VERSION 0.1.0 LANGUAGES C CXX) + +add_library(commander "src/commander.cc") +install(TARGETS commander DESTINATION lib) +install(FILES "src/commander.h" DESTINATION include) + +add_executable(test src/main.cc) +target_link_libraries(test commander) \ No newline at end of file diff --git a/src/commander.cc b/src/commander.cc new file mode 100644 index 0000000..8de7a24 --- /dev/null +++ b/src/commander.cc @@ -0,0 +1,640 @@ +#include +#include +#include +#include +#include +#include +#include +#include "commander.h" + +#define DEFAULT_MESSAGE_MAX_LEN (30) +#define DEFAULT_ERROR_EXIT_CODE (1) + +class CommandImpl; +class OptionImpl; +class ArgumentImpl; + +using namespace commander; + +void assert(bool condition, const char *fmt, ...) +{ + if (!condition) + { + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); + exit(DEFAULT_ERROR_EXIT_CODE); + } +} + +class CastClass +{ +public: + void *m_ptr; +}; + +#define CMD (static_cast(m_ptr)) +#define OPT (static_cast(m_ptr)) +#define ARG (static_cast(m_ptr)) +#define CAST_TO(clazz, ptr) (static_cast(((CastClass *)(ptr))->m_ptr)) +inline CommandImpl *CMD_EX(Command &cmd) { return CAST_TO(CommandImpl, &cmd); } +inline CommandImpl *CMD_EX(Command *cmd) { return CAST_TO(CommandImpl, cmd); } +inline OptionImpl *OPT_EX(Option &opt) { return CAST_TO(OptionImpl, &opt); } +inline OptionImpl *OPT_EX(Option *opt) { return CAST_TO(OptionImpl, opt); } +inline ArgumentImpl *ARG_EX(Argument &arg) { return CAST_TO(ArgumentImpl, &arg); } +inline ArgumentImpl *ARG_EX(Argument *arg) { return CAST_TO(ArgumentImpl, arg); } + +class OptionImpl +{ +public: + OptionImpl(const std::string &name) : m_name(name) {} + + inline const std::string &name() const { return m_name; } + + inline void shortcut(const std::string &shortcut) { m_shortcut = shortcut; } + inline const std::string &shortcut() const { return m_shortcut; } + + inline void description(const std::string &description) { m_description = description; } + inline const std::string &description() const { return m_description; } + + inline void required(bool required) { m_required = required; } + inline bool required() const { return m_required; } + + void boolean(bool boolean) { m_boolean = boolean; } + bool boolean() const { return m_boolean; } + + void valueName(const std::string &valueName) { m_valueName = valueName; } + const std::string &valueName() const { return m_valueName; } + + std::string helpMessage(int maxlen = DEFAULT_MESSAGE_MAX_LEN) + { + std::stringstream ss; + if (m_shortcut.size()) + ss << "-" << m_shortcut << ","; + ss << "--" << m_name; + if (!m_boolean) + { + ss << " "; + if (m_required) + ss << "<"; + else + ss << "["; + + if (m_valueName.size()) + ss << m_valueName; + else + ss << m_name; + + if (m_required) + ss << ">"; + else + ss << "]"; + } + std::string result = ss.str(); + + if (m_description.size()) + { + std::stringstream pad; + for (size_t i = result.size(); i < maxlen; i++) + pad << " "; + pad << m_description; + result += pad.str(); + } + return result; + } + +private: + std::string m_name; + std::string m_shortcut; + std::string m_description; + std::string m_valueName; + bool m_required = false; + bool m_boolean = false; +}; + +class ArgumentImpl +{ +public: + inline ArgumentImpl(const std::string &name) : m_name(name) {} + + inline const std::string &name() const { return m_name; } + + inline void description(const std::string &description) { m_description = description; } + inline const std::string &description() const { return m_description; } + + inline void required(bool required) { m_required = required; } + inline bool required() const { return m_required; } + + std::string helpMessage(int maxlen = DEFAULT_MESSAGE_MAX_LEN) + { + std::string result = m_name; + + if (m_description.size()) + { + std::stringstream pad; + for (size_t i = result.size(); i < maxlen; i++) + pad << " "; + pad << m_description; + result += pad.str(); + } + return result; + } + +private: + std::string m_name; + std::string m_description; + bool m_required = false; +}; + +class CommandImpl +{ +public: + CommandImpl(const std::string &name) : m_name(name) + { + option("help").shortcut("h").boolean().description("Print this help message"); + } + + const std::string &name() const { return m_name; } + + inline void execute(ExecuteCallback callback) { m_excute = callback; } + inline ExecuteCallback execute() const { return m_excute; } + + inline void description(const std::string &description) { m_description = description; } + inline const std::string &description() const { return m_description; } + + Command &command(const std::string &name) + { + if (m_commands.find(name) == m_commands.end()) + { + m_commands.emplace(name, std::make_shared(name)); + } + return *m_commands[name]; + } + + Option &option(const std::string &name) + { + if (m_options.find(name) == m_options.end()) + m_options.emplace(name, std::make_shared