2023-02-13 10:57:10 +08:00
|
|
|
#pragma once
|
|
|
|
|
2023-08-27 12:30:12 +08:00
|
|
|
#include <VTAll.hpp>
|
2023-02-13 10:57:10 +08:00
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
2023-08-27 12:30:12 +08:00
|
|
|
#include <functional>
|
2023-03-03 11:06:26 +08:00
|
|
|
#include <deque>
|
2024-08-24 21:27:23 +08:00
|
|
|
#include <map>
|
2023-08-27 12:30:12 +08:00
|
|
|
#include <stdexcept>
|
|
|
|
#include <cinttypes>
|
|
|
|
#include <initializer_list>
|
2024-08-24 21:27:23 +08:00
|
|
|
#include <type_traits>
|
|
|
|
#include <memory>
|
2023-03-03 11:06:26 +08:00
|
|
|
|
|
|
|
namespace Unvirt::CmdHelper {
|
|
|
|
|
|
|
|
class CmdSplitter {
|
|
|
|
public:
|
2024-08-24 21:27:23 +08:00
|
|
|
using Result_t = std::deque<std::u8string>;
|
|
|
|
private:
|
2023-03-03 11:06:26 +08:00
|
|
|
enum class StateType : int {
|
|
|
|
SPACE,
|
|
|
|
SINGLE,
|
|
|
|
DOUBLE,
|
|
|
|
ESCAPE,
|
|
|
|
NORMAL
|
|
|
|
};
|
2024-08-24 21:27:23 +08:00
|
|
|
|
2023-08-27 12:30:12 +08:00
|
|
|
public:
|
|
|
|
CmdSplitter() :
|
2024-08-24 21:27:23 +08:00
|
|
|
m_CurrentChar(u8'\0'), m_Buffer(), m_Result(), m_ValidResult(false),
|
|
|
|
m_State(StateType::NORMAL), m_PrevState(StateType::NORMAL) {}
|
2023-08-27 12:30:12 +08:00
|
|
|
~CmdSplitter() {}
|
2024-08-17 20:43:27 +08:00
|
|
|
YYCC_DEL_CLS_COPY_MOVE(CmdSplitter);
|
2023-08-27 12:30:12 +08:00
|
|
|
|
2024-08-24 21:27:23 +08:00
|
|
|
bool Convert(const std::u8string& u8cmd);
|
|
|
|
const Result_t& GetResult() const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
void ProcSpace();
|
|
|
|
void ProcSingle();
|
|
|
|
void ProcDouble();
|
|
|
|
void ProcEscape();
|
|
|
|
void ProcNormal();
|
|
|
|
|
|
|
|
char8_t m_CurrentChar;
|
|
|
|
std::u8string m_Buffer;
|
|
|
|
Result_t m_Result;
|
|
|
|
bool m_ValidResult;
|
|
|
|
StateType m_State, m_PrevState;
|
|
|
|
};
|
|
|
|
|
|
|
|
#pragma region ArgumentsMap
|
|
|
|
|
|
|
|
namespace ArgumentsMapItem {
|
|
|
|
|
|
|
|
class AbstractItem {
|
|
|
|
public:
|
|
|
|
AbstractItem() {}
|
|
|
|
virtual ~AbstractItem() {}
|
|
|
|
YYCC_DEF_CLS_COPY_MOVE(AbstractItem);
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty>, int> = 0>
|
|
|
|
class ArithmeticItem : public AbstractItem {
|
|
|
|
public:
|
|
|
|
ArithmeticItem(_Ty value) : AbstractItem(), m_Data(value) {}
|
|
|
|
virtual ~ArithmeticItem() {}
|
|
|
|
YYCC_DEF_CLS_COPY_MOVE(ArithmeticItem);
|
|
|
|
public:
|
|
|
|
_Ty Get() const { return m_Data; }
|
|
|
|
protected:
|
|
|
|
_Ty m_Data;
|
|
|
|
};
|
2023-08-27 12:30:12 +08:00
|
|
|
|
2024-08-24 21:27:23 +08:00
|
|
|
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty>, int> = 0>
|
|
|
|
class ArithmeticArrayItem : public AbstractItem {
|
|
|
|
public:
|
|
|
|
ArithmeticArrayItem(const std::vector<_Ty>& values) : AbstractItem(), m_Data(values) {}
|
|
|
|
virtual ~ArithmeticArrayItem() {}
|
|
|
|
YYCC_DEF_CLS_COPY_MOVE(ArithmeticArrayItem);
|
|
|
|
public:
|
|
|
|
const std::vector<_Ty>& Get() const { return m_Data; }
|
|
|
|
protected:
|
|
|
|
std::vector<_Ty> m_Data;
|
|
|
|
};
|
|
|
|
|
|
|
|
class StringItem : public AbstractItem {
|
|
|
|
public:
|
|
|
|
StringItem(const std::u8string_view& value) : AbstractItem(), m_Data(value) {}
|
|
|
|
virtual ~StringItem() {}
|
|
|
|
YYCC_DEF_CLS_COPY_MOVE(StringItem);
|
|
|
|
public:
|
|
|
|
const std::u8string& Get() const { return m_Data; }
|
|
|
|
protected:
|
|
|
|
std::u8string m_Data;
|
|
|
|
};
|
2023-03-03 11:06:26 +08:00
|
|
|
|
2024-08-24 21:27:23 +08:00
|
|
|
class StringArrayItem : public AbstractItem {
|
|
|
|
public:
|
|
|
|
StringArrayItem(const std::vector<std::u8string>& value) : AbstractItem(), m_Data(value) {}
|
|
|
|
virtual ~StringArrayItem() {}
|
|
|
|
YYCC_DEF_CLS_COPY_MOVE(StringArrayItem);
|
|
|
|
public:
|
|
|
|
const std::vector<std::u8string>& Get() const { return m_Data; }
|
|
|
|
protected:
|
|
|
|
std::vector<std::u8string> m_Data;
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
class ArgumentsMap {
|
|
|
|
public:
|
|
|
|
ArgumentsMap() : m_Data() {}
|
|
|
|
~ArgumentsMap() {}
|
|
|
|
YYCC_DEF_CLS_COPY_MOVE(ArgumentsMap);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
std::map<std::u8string, std::unique_ptr<ArgumentsMapItem::AbstractItem>> m_Data;
|
|
|
|
|
|
|
|
public:
|
|
|
|
template<class _Ty, class... _Types, std::enable_if_t<std::is_base_of_v<ArgumentsMapItem::AbstractItem, _Ty>, int> = 0>
|
|
|
|
void Add(const std::u8string_view& key, _Types&&... args) {
|
|
|
|
// check argument
|
|
|
|
if (key.empty())
|
|
|
|
throw std::invalid_argument("argument key should not be empty");
|
|
|
|
// insert into data
|
|
|
|
auto result = m_Data.try_emplace(std::u8string(key), std::make_unique<_Ty>(std::forward<_Types>(args)...));
|
|
|
|
if (!result.second)
|
|
|
|
throw std::runtime_error("try to add an existing key.");
|
|
|
|
}
|
|
|
|
template<class _Ty, std::enable_if_t<std::is_base_of_v<ArgumentsMapItem::AbstractItem, _Ty>, int> = 0>
|
|
|
|
const _Ty& Get() const {
|
|
|
|
// check argument
|
|
|
|
if (key.empty())
|
|
|
|
throw std::invalid_argument("argument key should not be empty");
|
|
|
|
// find key first
|
|
|
|
auto finder = m_Data.find(std::u8string(key));
|
|
|
|
if (finder == m_Data.end())
|
|
|
|
throw std::runtime_error("try to get a non-existent key.");
|
|
|
|
// get stored value data
|
|
|
|
const ArgumentsMapItem::AbstractItem& value = *finder->second.get();
|
|
|
|
return static_cast<const _Ty&>(value);
|
|
|
|
}
|
|
|
|
void Remove(const std::u8string_view& key) {
|
|
|
|
// check argument
|
|
|
|
if (key.empty())
|
|
|
|
throw std::invalid_argument("argument key should not be empty");
|
|
|
|
// remove and return remove result.
|
|
|
|
if (m_Data.erase(std::u8string(key)) == 0u)
|
|
|
|
throw std::runtime_error("try to delete a non-existent key.");
|
|
|
|
}
|
2023-08-27 12:30:12 +08:00
|
|
|
};
|
|
|
|
|
2024-08-24 21:27:23 +08:00
|
|
|
#pragma endregion
|
|
|
|
|
2023-08-27 12:30:12 +08:00
|
|
|
class HelpDocument {
|
|
|
|
public:
|
2023-08-27 14:21:44 +08:00
|
|
|
HelpDocument();
|
|
|
|
~HelpDocument();
|
2024-08-24 21:27:23 +08:00
|
|
|
YYCC_DEF_CLS_COPY_MOVE(HelpDocument);
|
2023-08-27 12:30:12 +08:00
|
|
|
|
2024-08-24 21:27:23 +08:00
|
|
|
public:
|
|
|
|
void Push(const std::u8string& arg_name, const std::u8string& arg_desc);
|
2023-08-27 14:21:44 +08:00
|
|
|
void Pop();
|
2024-08-24 21:27:23 +08:00
|
|
|
void Terminate(std::u8string& command_desc);
|
2023-08-27 12:30:12 +08:00
|
|
|
void Print();
|
|
|
|
|
|
|
|
protected:
|
|
|
|
struct StackItem {
|
2024-08-24 21:27:23 +08:00
|
|
|
StackItem();
|
|
|
|
StackItem(const std::u8string& name, const std::u8string& desc);
|
2024-08-17 20:43:27 +08:00
|
|
|
YYCC_DEF_CLS_COPY_MOVE(StackItem);
|
2024-08-24 21:27:23 +08:00
|
|
|
|
|
|
|
std::u8string m_Name;
|
|
|
|
std::u8string m_Desc;
|
2023-08-27 12:30:12 +08:00
|
|
|
};
|
|
|
|
std::deque<StackItem> m_Stack;
|
2024-08-24 21:27:23 +08:00
|
|
|
|
2023-08-27 12:30:12 +08:00
|
|
|
struct ResultItem {
|
2024-08-24 21:27:23 +08:00
|
|
|
ResultItem();
|
|
|
|
ResultItem(const std::u8string& cmd_desc, const std::deque<StackItem>& arg_desc);
|
2024-08-17 20:43:27 +08:00
|
|
|
YYCC_DEF_CLS_COPY_MOVE(ResultItem);
|
2024-08-24 21:27:23 +08:00
|
|
|
|
|
|
|
std::u8string m_CmdDesc;
|
2023-08-27 12:30:12 +08:00
|
|
|
std::vector<StackItem> m_ArgDesc;
|
|
|
|
};
|
|
|
|
std::vector<ResultItem> m_Results;
|
|
|
|
};
|
|
|
|
|
|
|
|
class AbstractNode {
|
2024-08-24 21:27:23 +08:00
|
|
|
friend class CommandRoot;
|
|
|
|
public:
|
|
|
|
using ExecutionFct = void(*)(const ArgumentsMap&);
|
|
|
|
|
2023-08-27 12:30:12 +08:00
|
|
|
public:
|
2023-08-27 14:21:44 +08:00
|
|
|
AbstractNode();
|
|
|
|
virtual ~AbstractNode();
|
2024-08-24 21:27:23 +08:00
|
|
|
YYCC_DEF_CLS_COPY_MOVE(AbstractNode);
|
2023-08-27 12:30:12 +08:00
|
|
|
|
|
|
|
AbstractNode* Then(AbstractNode*);
|
2023-08-27 14:21:44 +08:00
|
|
|
AbstractNode* Executes(ExecutionFct, const char* = nullptr);
|
|
|
|
AbstractNode* Comment(const char*);
|
2023-08-27 12:30:12 +08:00
|
|
|
|
2023-08-27 14:21:44 +08:00
|
|
|
protected:
|
2024-08-24 21:27:23 +08:00
|
|
|
void Help(HelpDocument& doc);
|
|
|
|
bool Consume(CmdSplitter::Result_t& cmds, ArgumentsMap& am);
|
|
|
|
virtual bool IsConflictWith(AbstractNode* node) = 0;
|
|
|
|
virtual bool IsArgument() = 0;
|
|
|
|
virtual std::u8string HelpSymbol() = 0;
|
|
|
|
virtual bool BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) = 0;
|
|
|
|
virtual void EndConsume(ArgumentsMap*) = 0;
|
2023-08-27 12:30:12 +08:00
|
|
|
|
2024-08-24 21:27:23 +08:00
|
|
|
protected:
|
|
|
|
std::vector<std::unique_ptr<AbstractNode>> m_Nodes;
|
2023-08-27 12:30:12 +08:00
|
|
|
ExecutionFct m_Execution;
|
2023-08-27 14:21:44 +08:00
|
|
|
std::string m_ExecutionDesc;
|
2023-08-27 12:30:12 +08:00
|
|
|
std::string m_Comment;
|
|
|
|
};
|
|
|
|
|
|
|
|
class CommandRoot : public AbstractNode {
|
|
|
|
public:
|
2023-08-27 14:21:44 +08:00
|
|
|
CommandRoot();
|
|
|
|
virtual ~CommandRoot();
|
2024-08-17 20:43:27 +08:00
|
|
|
YYCC_DEL_CLS_COPY_MOVE(CommandRoot);
|
2023-08-27 12:30:12 +08:00
|
|
|
|
|
|
|
// Root use special consume and help functions.
|
|
|
|
bool RootConsume(std::deque<std::string>&);
|
|
|
|
HelpDocument* RootHelp();
|
|
|
|
|
2023-08-27 14:21:44 +08:00
|
|
|
public:
|
|
|
|
virtual NodeType GetNodeType() override { throw std::logic_error("Root can not be called."); }
|
2023-08-27 12:30:12 +08:00
|
|
|
virtual bool IsConflictWith(AbstractNode*) override { throw std::logic_error("Root can not be called."); }
|
2023-08-27 14:21:44 +08:00
|
|
|
protected:
|
|
|
|
virtual std::string GetHelpSymbol() override { throw std::logic_error("Root can not be called."); }
|
|
|
|
virtual bool BeginAccept(const std::string&, ArgumentsMap*) override { throw std::logic_error("Root can not be called."); }
|
|
|
|
virtual void EndAccept(ArgumentsMap*) override { throw std::logic_error("Root can not be called."); }
|
2023-08-27 12:30:12 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
class Literal : public AbstractNode {
|
|
|
|
friend class Choice;
|
2023-08-27 14:21:44 +08:00
|
|
|
friend class AbstractArgument;
|
2023-08-27 12:30:12 +08:00
|
|
|
public:
|
2023-08-27 14:21:44 +08:00
|
|
|
Literal(const char* words);
|
|
|
|
virtual ~Literal();
|
2024-08-17 20:43:27 +08:00
|
|
|
YYCC_DEL_CLS_COPY_MOVE(Literal);
|
2023-08-27 12:30:12 +08:00
|
|
|
|
2023-08-27 14:21:44 +08:00
|
|
|
public:
|
|
|
|
virtual NodeType GetNodeType() override;
|
|
|
|
virtual bool IsConflictWith(AbstractNode* node) override;
|
2023-08-27 12:30:12 +08:00
|
|
|
protected:
|
2023-08-27 14:21:44 +08:00
|
|
|
virtual std::string GetHelpSymbol() override;
|
|
|
|
virtual bool BeginAccept(const std::string&, ArgumentsMap*) override;
|
|
|
|
virtual void EndAccept(ArgumentsMap*) override;
|
2023-08-27 12:30:12 +08:00
|
|
|
|
|
|
|
std::string m_Literal;
|
|
|
|
};
|
|
|
|
|
|
|
|
class Choice : public AbstractNode {
|
2023-08-27 14:21:44 +08:00
|
|
|
friend class Literal;
|
|
|
|
friend class AbstractArgument;
|
2023-08-27 12:30:12 +08:00
|
|
|
public:
|
2023-08-27 16:45:07 +08:00
|
|
|
using vType = size_t;
|
2023-08-27 14:21:44 +08:00
|
|
|
Choice(const char* argname, const std::initializer_list<std::string>& vocabulary);
|
|
|
|
virtual ~Choice();
|
2024-08-17 20:43:27 +08:00
|
|
|
YYCC_DEL_CLS_COPY_MOVE(Choice);
|
2023-08-27 12:30:12 +08:00
|
|
|
|
2023-08-27 16:45:07 +08:00
|
|
|
vType* GetIndex();
|
2023-08-27 12:30:12 +08:00
|
|
|
|
2023-08-27 14:21:44 +08:00
|
|
|
public:
|
|
|
|
virtual NodeType GetNodeType() override;
|
|
|
|
virtual bool IsConflictWith(AbstractNode* node) override;
|
|
|
|
protected:
|
|
|
|
virtual std::string GetHelpSymbol() override;
|
|
|
|
virtual bool BeginAccept(const std::string&, ArgumentsMap*) override;
|
|
|
|
virtual void EndAccept(ArgumentsMap*) override;
|
2023-08-27 12:30:12 +08:00
|
|
|
|
|
|
|
std::string m_ChoiceName;
|
|
|
|
std::vector<std::string> m_Vocabulary;
|
2023-08-27 14:21:44 +08:00
|
|
|
bool m_Accepted;
|
|
|
|
size_t m_GottenIndex;
|
2023-08-27 12:30:12 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
class AbstractArgument : public AbstractNode {
|
2023-08-27 14:21:44 +08:00
|
|
|
friend class Literal;
|
|
|
|
friend class Choice;
|
2023-08-27 12:30:12 +08:00
|
|
|
public:
|
2023-08-27 14:21:44 +08:00
|
|
|
AbstractArgument(const char* argname);
|
|
|
|
virtual ~AbstractArgument();
|
2024-08-17 20:43:27 +08:00
|
|
|
YYCC_DEL_CLS_COPY_MOVE(AbstractArgument);
|
2023-08-27 12:30:12 +08:00
|
|
|
|
|
|
|
template<class T>
|
2024-08-24 21:27:23 +08:00
|
|
|
T GetData() {
|
|
|
|
return reinterpret_cast<T>(m_ParsedData);
|
2023-08-27 14:21:44 +08:00
|
|
|
}
|
2023-08-27 12:30:12 +08:00
|
|
|
|
2023-08-27 14:21:44 +08:00
|
|
|
public:
|
|
|
|
virtual NodeType GetNodeType() override;
|
|
|
|
virtual bool IsConflictWith(AbstractNode* node) override;
|
2023-08-27 12:30:12 +08:00
|
|
|
protected:
|
2023-08-27 14:21:44 +08:00
|
|
|
virtual std::string GetHelpSymbol() override;
|
|
|
|
virtual bool BeginAccept(const std::string&, ArgumentsMap*) override;
|
|
|
|
virtual void EndAccept(ArgumentsMap*) override;
|
|
|
|
|
2023-08-27 12:30:12 +08:00
|
|
|
virtual bool BeginParse(const std::string&) = 0;
|
|
|
|
virtual void EndParse() = 0;
|
|
|
|
|
|
|
|
std::string m_ArgName;
|
|
|
|
bool m_Accepted;
|
|
|
|
void* m_ParsedData;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Return true mean this value can accept.
|
|
|
|
*/
|
|
|
|
using IntLimit = std::function<bool(int32_t)>;
|
|
|
|
class IntArgument : public AbstractArgument {
|
|
|
|
public:
|
2023-08-27 14:21:44 +08:00
|
|
|
using vType = int32_t;
|
|
|
|
IntArgument(const char* argname, IntLimit limit = nullptr) :
|
|
|
|
AbstractArgument(argname), m_IntLimit(limit) {}
|
2023-08-27 12:30:12 +08:00
|
|
|
virtual ~IntArgument() {}
|
2024-08-17 20:43:27 +08:00
|
|
|
YYCC_DEL_CLS_COPY_MOVE(IntArgument);
|
2023-08-27 12:30:12 +08:00
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual bool BeginParse(const std::string&) override;
|
|
|
|
virtual void EndParse() override;
|
|
|
|
|
|
|
|
IntLimit m_IntLimit;
|
2023-03-03 11:06:26 +08:00
|
|
|
};
|
2023-02-13 10:57:10 +08:00
|
|
|
|
2023-08-27 12:30:12 +08:00
|
|
|
class StringArgument : public AbstractArgument {
|
2023-03-03 11:06:26 +08:00
|
|
|
public:
|
2023-08-27 14:21:44 +08:00
|
|
|
using vType = std::string;
|
2024-08-24 21:27:23 +08:00
|
|
|
StringArgument(const char* argname) :
|
2023-08-27 22:14:02 +08:00
|
|
|
AbstractArgument(argname) {}
|
2023-08-27 12:30:12 +08:00
|
|
|
virtual ~StringArgument() {}
|
2024-08-17 20:43:27 +08:00
|
|
|
YYCC_DEL_CLS_COPY_MOVE(StringArgument);
|
2023-08-27 12:30:12 +08:00
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual bool BeginParse(const std::string&) override;
|
|
|
|
virtual void EndParse() override;
|
2023-03-03 11:06:26 +08:00
|
|
|
};
|
2023-02-13 16:52:00 +08:00
|
|
|
|
2023-08-27 12:30:12 +08:00
|
|
|
class EncodingArgument : public AbstractArgument {
|
2023-03-03 11:06:26 +08:00
|
|
|
public:
|
2023-08-27 14:21:44 +08:00
|
|
|
using vType = std::vector<std::string>;
|
2024-08-24 21:27:23 +08:00
|
|
|
EncodingArgument(const char* argname) :
|
2023-08-27 22:14:02 +08:00
|
|
|
AbstractArgument(argname) {}
|
2023-08-27 12:30:12 +08:00
|
|
|
virtual ~EncodingArgument() {}
|
2024-08-17 20:43:27 +08:00
|
|
|
YYCC_DEL_CLS_COPY_MOVE(EncodingArgument);
|
2023-08-27 12:30:12 +08:00
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual bool BeginParse(const std::string&) override;
|
|
|
|
virtual void EndParse() override;
|
2023-03-03 11:06:26 +08:00
|
|
|
};
|
2023-02-13 22:13:30 +08:00
|
|
|
|
2023-08-27 12:30:12 +08:00
|
|
|
}
|