123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- /*
- * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
- *
- * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
- *
- * Use of this source code is governed by MIT license that can be found in the
- * LICENSE file in the root of the source tree. All contributing project authors
- * may be found in the AUTHORS file in the root of the source tree.
- */
- #ifndef SRC_UTIL_CMD_H_
- #define SRC_UTIL_CMD_H_
- #include <map>
- #include <mutex>
- #include <string>
- #include <memory>
- #include <vector>
- #include <iostream>
- #include <functional>
- #include "mini.h"
- namespace toolkit{
- class Option {
- public:
- using OptionHandler = std::function<bool(const std::shared_ptr<std::ostream> &stream, const std::string &arg)>;
- enum ArgType {
- ArgNone = 0,//no_argument,
- ArgRequired = 1,//required_argument,
- ArgOptional = 2,//optional_argument
- };
- Option() = default;
- Option(char short_opt, const char *long_opt, enum ArgType type, const char *default_value, bool must_exist,
- const char *des, const OptionHandler &cb) {
- _short_opt = short_opt;
- _long_opt = long_opt;
- _type = type;
- if (type != ArgNone) {
- if (default_value) {
- _default_value = std::make_shared<std::string>(default_value);
- }
- if (!_default_value && must_exist) {
- _must_exist = true;
- }
- }
- _des = des;
- _cb = cb;
- }
- bool operator()(const std::shared_ptr<std::ostream> &stream, const std::string &arg) {
- return _cb ? _cb(stream, arg) : true;
- }
- private:
- friend class OptionParser;
- bool _must_exist = false;
- char _short_opt;
- enum ArgType _type;
- std::string _des;
- std::string _long_opt;
- OptionHandler _cb;
- std::shared_ptr<std::string> _default_value;
- };
- class OptionParser {
- public:
- using OptionCompleted = std::function<void(const std::shared_ptr<std::ostream> &, mINI &)>;
- OptionParser(const OptionCompleted &cb = nullptr, bool enable_empty_args = true) {
- _on_completed = cb;
- _enable_empty_args = enable_empty_args;
- _helper = Option('h', "help", Option::ArgNone, nullptr, false, "打印此信息",
- [this](const std::shared_ptr<std::ostream> &stream,const std::string &arg)->bool {
- static const char *argsType[] = {"无参", "有参", "选参"};
- static const char *mustExist[] = {"选填", "必填"};
- static std::string defaultPrefix = "默认:";
- static std::string defaultNull = "null";
- std::stringstream printer;
- size_t maxLen_longOpt = 0;
- auto maxLen_default = defaultNull.size();
- for (auto &pr : _map_options) {
- auto &opt = pr.second;
- if (opt._long_opt.size() > maxLen_longOpt) {
- maxLen_longOpt = opt._long_opt.size();
- }
- if (opt._default_value) {
- if (opt._default_value->size() > maxLen_default) {
- maxLen_default = opt._default_value->size();
- }
- }
- }
- for (auto &pr : _map_options) {
- auto &opt = pr.second;
- //打印短参和长参名
- if (opt._short_opt) {
- printer << " -" << opt._short_opt << " --" << opt._long_opt;
- } else {
- printer << " " << " " << " --" << opt._long_opt;
- }
- for (size_t i = 0; i < maxLen_longOpt - opt._long_opt.size(); ++i) {
- printer << " ";
- }
- //打印是否有参
- printer << " " << argsType[opt._type];
- //打印默认参数
- std::string defaultValue = defaultNull;
- if (opt._default_value) {
- defaultValue = *opt._default_value;
- }
- printer << " " << defaultPrefix << defaultValue;
- for (size_t i = 0; i < maxLen_default - defaultValue.size(); ++i) {
- printer << " ";
- }
- //打印是否必填参数
- printer << " " << mustExist[opt._must_exist];
- //打印描述
- printer << " " << opt._des << std::endl;
- }
- throw std::invalid_argument(printer.str());
- });
- (*this) << _helper;
- }
- OptionParser &operator<<(Option &&option) {
- int index = 0xFF + (int) _map_options.size();
- if (option._short_opt) {
- _map_char_index.emplace(option._short_opt, index);
- }
- _map_options.emplace(index, std::forward<Option>(option));
- return *this;
- }
- OptionParser &operator<<(const Option &option) {
- int index = 0xFF + (int) _map_options.size();
- if (option._short_opt) {
- _map_char_index.emplace(option._short_opt, index);
- }
- _map_options.emplace(index, option);
- return *this;
- }
- void delOption(const char *key) {
- for (auto &pr : _map_options) {
- if (pr.second._long_opt == key) {
- if (pr.second._short_opt) {
- _map_char_index.erase(pr.second._short_opt);
- }
- _map_options.erase(pr.first);
- break;
- }
- }
- }
- void operator ()(mINI &all_args, int argc, char *argv[], const std::shared_ptr<std::ostream> &stream);
- private:
- bool _enable_empty_args;
- Option _helper;
- std::map<char, int> _map_char_index;
- std::map<int, Option> _map_options;
- OptionCompleted _on_completed;
- };
- class CMD : public mINI {
- public:
- virtual ~CMD() = default;
- virtual const char *description() const {
- return "description";
- }
- void operator()(int argc, char *argv[], const std::shared_ptr<std::ostream> &stream = nullptr) {
- this->clear();
- std::shared_ptr<std::ostream> coutPtr(&std::cout, [](std::ostream *) {});
- (*_parser)(*this, argc, argv, stream ? stream : coutPtr);
- }
- bool hasKey(const char *key) {
- return this->find(key) != this->end();
- }
- std::vector<variant> splitedVal(const char *key, const char *delim = ":") {
- std::vector<variant> ret;
- auto &val = (*this)[key];
- split(val, delim, ret);
- return ret;
- }
- void delOption(const char *key) {
- if (_parser) {
- _parser->delOption(key);
- }
- }
- protected:
- std::shared_ptr<OptionParser> _parser;
- private:
- void split(const std::string &s, const char *delim, std::vector<variant> &ret) {
- size_t last = 0;
- auto index = s.find(delim, last);
- while (index != std::string::npos) {
- if (index - last > 0) {
- ret.push_back(s.substr(last, index - last));
- }
- last = index + strlen(delim);
- index = s.find(delim, last);
- }
- if (s.size() - last > 0) {
- ret.push_back(s.substr(last));
- }
- }
- };
- class CMDRegister {
- public:
- static CMDRegister &Instance();
- void clear() {
- std::lock_guard<std::recursive_mutex> lck(_mtx);
- _cmd_map.clear();
- }
- void registCMD(const char *name, const std::shared_ptr<CMD> &cmd) {
- std::lock_guard<std::recursive_mutex> lck(_mtx);
- _cmd_map.emplace(name, cmd);
- }
- void unregistCMD(const char *name) {
- std::lock_guard<std::recursive_mutex> lck(_mtx);
- _cmd_map.erase(name);
- }
- std::shared_ptr<CMD> operator[](const char *name) {
- std::lock_guard<std::recursive_mutex> lck(_mtx);
- auto it = _cmd_map.find(name);
- if (it == _cmd_map.end()) {
- throw std::invalid_argument(std::string("CMD not existed: ") + name);
- }
- return it->second;
- }
- void operator()(const char *name, int argc, char *argv[], const std::shared_ptr<std::ostream> &stream = nullptr) {
- auto cmd = (*this)[name];
- if (!cmd) {
- throw std::invalid_argument(std::string("CMD not existed: ") + name);
- }
- (*cmd)(argc, argv, stream);
- }
- void printHelp(const std::shared_ptr<std::ostream> &streamTmp = nullptr) {
- auto stream = streamTmp;
- if (!stream) {
- stream.reset(&std::cout, [](std::ostream *) {});
- }
- std::lock_guard<std::recursive_mutex> lck(_mtx);
- size_t maxLen = 0;
- for (auto &pr : _cmd_map) {
- if (pr.first.size() > maxLen) {
- maxLen = pr.first.size();
- }
- }
- for (auto &pr : _cmd_map) {
- (*stream) << " " << pr.first;
- for (size_t i = 0; i < maxLen - pr.first.size(); ++i) {
- (*stream) << " ";
- }
- (*stream) << " " << pr.second->description() << std::endl;
- }
- }
- void operator()(const std::string &line, const std::shared_ptr<std::ostream> &stream = nullptr) {
- if (line.empty()) {
- return;
- }
- std::vector<char *> argv;
- size_t argc = getArgs((char *) line.data(), argv);
- if (argc == 0) {
- return;
- }
- std::string cmd = argv[0];
- std::lock_guard<std::recursive_mutex> lck(_mtx);
- auto it = _cmd_map.find(cmd);
- if (it == _cmd_map.end()) {
- std::stringstream ss;
- ss << " 未识别的命令\"" << cmd << "\",输入 \"help\" 获取帮助.";
- throw std::invalid_argument(ss.str());
- }
- (*it->second)((int) argc, &argv[0], stream);
- }
- private:
- size_t getArgs(char *buf, std::vector<char *> &argv) {
- size_t argc = 0;
- bool start = false;
- auto len = strlen(buf);
- for (size_t i = 0; i < len; ++i) {
- if (buf[i] != ' ' && buf[i] != '\t' && buf[i] != '\r' && buf[i] != '\n') {
- if (!start) {
- start = true;
- if (argv.size() < argc + 1) {
- argv.resize(argc + 1);
- }
- argv[argc++] = buf + i;
- }
- } else {
- buf[i] = '\0';
- start = false;
- }
- }
- return argc;
- }
- private:
- std::recursive_mutex _mtx;
- std::map<std::string, std::shared_ptr<CMD> > _cmd_map;
- };
- //帮助命令(help),该命令默认已注册
- class CMD_help : public CMD {
- public:
- CMD_help() {
- _parser = std::make_shared<OptionParser>([](const std::shared_ptr<std::ostream> &stream, mINI &) {
- CMDRegister::Instance().printHelp(stream);
- });
- }
- const char *description() const override {
- return "打印帮助信息";
- }
- };
- class ExitException : public std::exception {};
- //退出程序命令(exit),该命令默认已注册
- class CMD_exit : public CMD {
- public:
- CMD_exit() {
- _parser = std::make_shared<OptionParser>([](const std::shared_ptr<std::ostream> &, mINI &) {
- throw ExitException();
- });
- }
- const char *description() const override {
- return "退出shell";
- }
- };
- //退出程序命令(quit),该命令默认已注册
- #define CMD_quit CMD_exit
- //清空屏幕信息命令(clear),该命令默认已注册
- class CMD_clear : public CMD {
- public:
- CMD_clear() {
- _parser = std::make_shared<OptionParser>([this](const std::shared_ptr<std::ostream> &stream, mINI &args) {
- clear(stream);
- });
- }
- const char *description() const {
- return "清空屏幕输出";
- }
- private:
- void clear(const std::shared_ptr<std::ostream> &stream) {
- (*stream) << "\x1b[2J\x1b[H";
- stream->flush();
- }
- };
- #define GET_CMD(name) (*(CMDRegister::Instance()[name]))
- #define CMD_DO(name,...) (*(CMDRegister::Instance()[name]))(__VA_ARGS__)
- #define REGIST_CMD(name) CMDRegister::Instance().registCMD(#name,std::make_shared<CMD_##name>());
- }//namespace toolkit
- #endif /* SRC_UTIL_CMD_H_ */
|