diff --git a/.gitignore b/.gitignore index e257658..1cee979 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,6 @@ *.exe *.out *.app +cmake-build-* +.idea diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7a207fd --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.22) + +project(parameter) + +set(CMAKE_CXX_STANDARD 17) + +include_directories(include) + +add_library(order + src/Options.cpp + src/Order.cpp + include/Order.h +) + +add_executable(myTest + test_main.cpp +) + +enable_testing() +find_package(GTest REQUIRED) +target_link_libraries(myTest PRIVATE GTest::GTest GTest::Main order) +set(GTEST_LIB gtest gtest_main) +add_test(NAME myTest COMMAND myTest) diff --git a/include/Options.h b/include/Options.h new file mode 100644 index 0000000..5b30fab --- /dev/null +++ b/include/Options.h @@ -0,0 +1,35 @@ +// +// Created by ling on 24-2-4. +// + +#ifndef OPTIONS_H +#define OPTIONS_H +#include +#include + +#include + +namespace ling { + class Order; + + class Options { + private: + Order *ptr = nullptr; + int id; + + public: + Options(int id, Order *ptr); + + [[nodiscard]] int64_t getInt64() const; + + [[nodiscard]] double getDouble() const; + + [[nodiscard]] const std::string &getString() const; + + [[nodiscard]] bool isOption() const; + + [[nodiscard]] bool isExistence() const; + }; +} // ling + +#endif //OPTIONS_H diff --git a/include/Order.h b/include/Order.h new file mode 100644 index 0000000..c396938 --- /dev/null +++ b/include/Order.h @@ -0,0 +1,86 @@ +// +// Created by ling on 24-2-4. +// + +#ifndef ORDER_H +#define ORDER_H + +#include +#include + +namespace ling { + class Options; + + class Order { + public: + enum Type { + OPT, + STRING, + INT, + DOUBLE, + }; + + private: + struct DataStruct { + std::vector options; + int id = 0; + Type type; + std::string memage; + + DataStruct(const std::vector &option, int id, Type type, const std::string &memage); + + DataStruct(); + }; + + std::vector cmd; + //选项 + std::unordered_map options; + //各种类型的参数 + std::unordered_map opt_str; + std::unordered_map opt_int; + std::unordered_map opt_double; + //参数类型索引 + std::unordered_map type; + + //匿名参数 + std::vector anonymity; + + //预定义参数规则 + std::unordered_map data; + //匿名参数数量 + int anonymityNumber = 0; + int nextID = 1; + std::string errStr; + + protected: + int64_t getInt64(int id) const; + + double getDouble(int id) const; + + const std::string &getString(int id) const; + + bool getOption(int id) const; + + bool isExistence(int id) const; + + public: + explicit Order(const std::vector &temp); + + Options addOption(const std::vector &opt, const std::string &message = ""); + + Options addOption(const std::vector &opt, Type type, const std::string &message = ""); + + /// 添加匿名参数 + void addAnonymity(int number); + + /// 获取匿名参数列表 + const std::vector &getAnonymity() const; + + /// 解析 + void analysis(); + + friend class Options; + }; +} // ling + +#endif //ORDER_H diff --git a/src/Options.cpp b/src/Options.cpp new file mode 100644 index 0000000..43f867d --- /dev/null +++ b/src/Options.cpp @@ -0,0 +1,32 @@ +// +// Created by ling on 24-2-4. +// + +#include "Options.h" + +namespace ling { + Options::Options(const int id, Order *const ptr) { + this->id = id; + this->ptr = ptr; + } + + int64_t Options::getInt64() const { + return ptr->getInt64(id); + } + + double Options::getDouble() const { + return ptr->getDouble(id); + } + + const std::string &Options::getString() const { + return ptr->getString(id); + } + + bool Options::isOption() const { + return ptr->getOption(id); + } + + bool Options::isExistence() const { + return ptr->isExistence(id); + } +} // ling diff --git a/src/Order.cpp b/src/Order.cpp new file mode 100644 index 0000000..4ea7788 --- /dev/null +++ b/src/Order.cpp @@ -0,0 +1,140 @@ +// +// Created by ling on 24-2-4. +// + +#include "../include/Order.h" + +#include + +namespace ling { + Order::DataStruct::DataStruct(const std::vector &option, const int id, const Type type, const std::string &memage) { + this->options = option; + this->id = id; + this->type = type; + this->memage = memage; + } + + Order::DataStruct::DataStruct() = default; + + int64_t Order::getInt64(const int id) const { + const auto it = opt_int.find(id); + if (it == opt_int.end()) { + return 0; + } + return it->second; + } + + double Order::getDouble(int id) const { + const auto it = opt_double.find(id); + if (it == opt_double.end()) { + return 0; + } + return it->second; + } + + const std::string &Order::getString(const int id) const { + const auto it = opt_str.find(id); + if (it == opt_str.end()) { + return errStr; + } + return it->second; + } + + bool Order::getOption(const int id) const { + const auto it = options.find(id); + if (it == options.end()) + throw std::runtime_error("无效键"); + return it->second; + } + + bool Order::isExistence(const int id) const { + if (options.find(id) != options.end()) { + return options.find(id)->second; + } + if (opt_int.find(id) != opt_int.end()) + return true; + if (opt_double.find(id) != opt_double.end()) + return true; + if (opt_str.find(id) != opt_str.end()) + return true; + return false; + } + + Order::Order(const std::vector &temp) { + this->cmd = temp; + } + + Options Order::addOption(const std::vector &opt, const std::string &message) { + return addOption(opt, OPT, message); + } + + Options Order::addOption(const std::vector &opt, Type type, const std::string &message) { + const int id = nextID++; + for (const auto &in: opt) + data[in] = DataStruct(opt, id, type, message); + + if (type == OPT) + options[id] = false; + return {id, this}; + } + + void Order::addAnonymity(const int number) { + this->anonymityNumber = number; + } + + const std::vector &Order::getAnonymity() const { + return anonymity; + } + + void Order::analysis() { + for (auto it = cmd.begin(); it != cmd.end(); ++it) { + auto rules = data.find(*it); + if (rules == data.end()) { + //检查匿名参数数量 + if (anonymity.size() >= anonymityNumber) + throw std::runtime_error("无法解析的参数:" + *it); + anonymity.push_back(*it); + continue; + } + if (it->at(0) != '-') + throw std::runtime_error("无法解析的参数:" + *it); + switch (rules->second.type) { + case OPT: + if (options[rules->second.id]) + throw std::runtime_error("重复提供参数:" + *it); + options[rules->second.id] = true; + break; + case STRING: + if (opt_str.find(rules->second.id) != opt_str.end()) + throw std::runtime_error("重复提供参数:" + *it); + if (std::next(it) == cmd.end()) + throw std::runtime_error("参数不足!" + *it + " 需要提供参数"); + if (std::next(it)->at(0) == '-') + throw std::runtime_error("参数不足!" + *it + " 需要提供参数"); + ++it; + opt_str[rules->second.id] = *it; + break; + case INT: + if (opt_int.find(rules->second.id) != opt_int.end()) + throw std::runtime_error("重复提供参数:" + *it); + if (std::next(it) == cmd.end()) + throw std::runtime_error("参数不足!" + *it + " 需要提供参数"); + if (std::next(it)->at(0) == '-') + throw std::runtime_error("参数不足!" + *it + " 需要提供参数"); + ++it; + opt_int[rules->second.id] = std::stoll(*it); + break; + case DOUBLE: + if (opt_double.find(rules->second.id) != opt_double.end()) + throw std::runtime_error("重复提供参数:" + *it); + if (std::next(it) == cmd.end()) + throw std::runtime_error("参数不足!" + *it + " 需要提供参数"); + if (std::next(it)->at(0) == '-') + throw std::runtime_error("参数不足!" + *it + " 需要提供参数"); + ++it; + opt_double[rules->second.id] = std::stod(*it); + break; + } + } + } +} // ling diff --git a/test_main.cpp b/test_main.cpp new file mode 100644 index 0000000..7146625 --- /dev/null +++ b/test_main.cpp @@ -0,0 +1,113 @@ +// +// Created by ling on 24-2-4. +// +#include +#include +#include + +TEST(Option, 选项测试) { + try { + ling::Order order({"-pts", "--all", "--ok"}); + const auto pts = order.addOption({"-pts", "--pts"}); + const auto all = order.addOption({"-all", "--all"}); + const auto ok = order.addOption({"--ok", "-ok"}); + const auto cancel = order.addOption({"--cancel"}); + order.analysis(); + ASSERT_TRUE(pts.isOption()); + ASSERT_TRUE(all.isOption()); + ASSERT_TRUE(ok.isOption()); + ASSERT_FALSE(cancel.isOption()); + } catch (const std::runtime_error &e) { + std::cout << e.what() << std::endl; + ASSERT_FALSE("解析出错!"); + } +} + +TEST(Option, 参数测试) { + try { + ling::Order order({"-pts", "hello world!", "--all", "--ok"}); + const auto pts = order.addOption({"-pts", "--pts"}, ling::Order::Type::STRING); + const auto all = order.addOption({"-all", "--all"}); + const auto ok = order.addOption({"--ok", "-ok"}); + const auto cancel = order.addOption({"--cancel"}); + order.analysis(); + ASSERT_TRUE(all.isOption()); + ASSERT_TRUE(ok.isOption()); + ASSERT_FALSE(cancel.isOption()); + ASSERT_TRUE(pts.getString() == "hello world!"); + } catch (const std::runtime_error &e) { + std::cout << e.what() << std::endl; + ASSERT_FALSE("解析出错!"); + } +} + + +TEST(Option, 异常参数测试) { + try { + ling::Order order({"-pts", "--all", "--ok"}); + const auto pts = order.addOption({"-pts", "--pts"}, ling::Order::Type::STRING); + const auto all = order.addOption({"-all", "--all"}); + const auto ok = order.addOption({"--ok", "-ok"}); + const auto cancel = order.addOption({"--cancel"}); + order.analysis(); + ASSERT_FALSE("纠正出错!"); + } catch (const std::runtime_error &e) { + } +} + +TEST(Option, 匿名参数测试) { + try { + ling::Order order({"-pts", "hello world!", "hello!", "--all", "--ok"}); + order.addAnonymity(1); + const auto pts = order.addOption({"-pts", "--pts"}, ling::Order::Type::STRING); + const auto all = order.addOption({"-all", "--all"}); + const auto ok = order.addOption({"--ok", "-ok"}); + const auto cancel = order.addOption({"--cancel"}); + order.analysis(); + ASSERT_TRUE(all.isOption()); + ASSERT_TRUE(ok.isOption()); + ASSERT_FALSE(cancel.isOption()); + ASSERT_TRUE(pts.getString() == "hello world!"); + ASSERT_TRUE(order.getAnonymity().size() == 1); + ASSERT_TRUE(order.getAnonymity()[0] == "hello!"); + } catch (const std::runtime_error &e) { + std::cout << e.what() << std::endl; + ASSERT_FALSE("解析出错!"); + } +} + +TEST(Option, 匿名参数测试2) { + try { + ling::Order order({"-pts", "hello world!", "hello!", "--all", "--ok", "world"}); + order.addAnonymity(2); + const auto pts = order.addOption({"-pts", "--pts"}, ling::Order::Type::STRING); + const auto all = order.addOption({"-all", "--all"}); + const auto ok = order.addOption({"--ok", "-ok"}); + const auto cancel = order.addOption({"--cancel"}); + order.analysis(); + ASSERT_TRUE(all.isOption()); + ASSERT_TRUE(ok.isOption()); + ASSERT_FALSE(cancel.isOption()); + ASSERT_TRUE(pts.getString() == "hello world!"); + ASSERT_TRUE(order.getAnonymity().size() == 2); + ASSERT_TRUE(order.getAnonymity()[0] == "hello!"); + ASSERT_TRUE(order.getAnonymity()[1] == "world"); + } catch (const std::runtime_error &e) { + std::cout << e.what() << std::endl; + ASSERT_FALSE("解析出错!"); + } +} + +TEST(Option, 匿名参数测试3) { + try { + ling::Order order({"-pts", "hello world!", "hello!", "--all", "--ok", "world"}); + order.addAnonymity(1); + const auto pts = order.addOption({"-pts", "--pts"}, ling::Order::Type::STRING); + const auto all = order.addOption({"-all", "--all"}); + const auto ok = order.addOption({"--ok", "-ok"}); + const auto cancel = order.addOption({"--cancel"}); + order.analysis(); + ASSERT_FALSE("匿名参数数量超出限制,但是没有抛出异常!"); + } catch (const std::runtime_error &e) { + } +}