CLI
This commit is contained in:
218
Src/IACore/inc/hpp/IACore/CLI.hpp
Normal file
218
Src/IACore/inc/hpp/IACore/CLI.hpp
Normal file
@ -0,0 +1,218 @@
|
||||
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||
// Copyright (C) 2024 IAS (ias@iasoft.dev)
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <IACore/Map.hpp>
|
||||
#include <IACore/String.hpp>
|
||||
#include <IACore/Vector.hpp>
|
||||
|
||||
namespace ia
|
||||
{
|
||||
class CLI
|
||||
{
|
||||
public:
|
||||
enum class ParameterValueType
|
||||
{
|
||||
INT,
|
||||
STRING,
|
||||
INT_LIST,
|
||||
STRING_LIST
|
||||
};
|
||||
|
||||
struct ParameterValue
|
||||
{
|
||||
ParameterValueType Type{};
|
||||
INT32 IntValue{};
|
||||
String StringValue{};
|
||||
Vector<INT32> IntListValue{};
|
||||
Vector<String> StringListValue{};
|
||||
};
|
||||
|
||||
struct ParameterDesc
|
||||
{
|
||||
String ID;
|
||||
String Help;
|
||||
BOOL IsOptional{};
|
||||
ParameterValueType Type{};
|
||||
};
|
||||
|
||||
struct SwitchDesc
|
||||
{
|
||||
String ID;
|
||||
String Help;
|
||||
};
|
||||
|
||||
struct CommandDesc
|
||||
{
|
||||
String ID;
|
||||
CHAR Shorthand{};
|
||||
String Help;
|
||||
Vector<SwitchDesc> Switches;
|
||||
Vector<ParameterDesc> Parameters;
|
||||
std::function<INT32(IN Map<String, ParameterValue> &¶meters, IN Map<String, BOOL> &&switchValues)>
|
||||
Action;
|
||||
};
|
||||
|
||||
public:
|
||||
CLI(IN CONST String &appName, IN IA_VERSION_TYPE appVersion, IN CONST String ©rightNotice)
|
||||
: m_appName(appName), m_appVersion(IA_STRINGIFY_VERSION(appVersion)), m_copyrightNotice(copyrightNotice)
|
||||
{
|
||||
RegisterCommand({
|
||||
.ID = "help",
|
||||
.Shorthand = 'h',
|
||||
.Help = "Displays this help menu",
|
||||
.Action =
|
||||
[&](IN Map<String, ParameterValue> &¶meters, IN Map<String, BOOL> &&switchValues) {
|
||||
DisplayHelp();
|
||||
return 0;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
INLINE INT32 Run(IN INT32 argc, IN PCCHAR argv[]);
|
||||
|
||||
protected:
|
||||
INLINE VOID RegisterCommand(IN CONST CommandDesc &desc);
|
||||
|
||||
private:
|
||||
INLINE VOID DisplayHelp();
|
||||
|
||||
private:
|
||||
CONST String m_appName;
|
||||
CONST String m_appVersion;
|
||||
CONST String m_copyrightNotice;
|
||||
Map<String, CommandDesc> m_commands;
|
||||
};
|
||||
} // namespace ia
|
||||
|
||||
namespace ia
|
||||
{
|
||||
INT32 CLI::Run(IN INT32 argc, IN PCCHAR argv[])
|
||||
{
|
||||
printf(__CC_WHITE "%s %s\n%s\n\n" __CC_DEFAULT, m_appName.c_str(), m_appVersion.c_str(),
|
||||
m_copyrightNotice.c_str());
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
DisplayHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!m_commands.contains(argv[1]))
|
||||
{
|
||||
printf(__CC_RED "No such known command \"%s\".\n" __CC_YELLOW
|
||||
"TIP: Use \"help\" to see available commands!" __CC_DEFAULT "\n",
|
||||
argv[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto cmd = m_commands[argv[1]];
|
||||
const auto requiredParamCount = (INT32) cmd.Parameters.size();
|
||||
if ((requiredParamCount << 1) >= (argc - 1))
|
||||
{
|
||||
printf(__CC_RED "Command \"%s\" requires at least %i parameter(s).\n" __CC_YELLOW
|
||||
"TIP: Use \"help\" to see correct usages!" __CC_DEFAULT "\n",
|
||||
argv[1], requiredParamCount);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Map<String, ParameterDesc> params;
|
||||
for (const auto &t : cmd.Parameters)
|
||||
params[t.ID] = t;
|
||||
|
||||
Map<String, ParameterValue> paramValues;
|
||||
|
||||
for (INT32 i = 2; i < argc; i++)
|
||||
{
|
||||
if (argv[i][0] == '-')
|
||||
{
|
||||
const auto paramName = &(argv[i][1]);
|
||||
if (!params.contains(paramName))
|
||||
{
|
||||
printf(__CC_RED "No such parameter \"%s\".\n" __CC_YELLOW
|
||||
"TIP: Use \"help\" to see correct usages!" __CC_DEFAULT "\n",
|
||||
argv[i]);
|
||||
return -3;
|
||||
}
|
||||
|
||||
const auto& p = params[paramName];
|
||||
|
||||
// Read parameter value
|
||||
const auto value = argv[++i];
|
||||
switch(p.Type)
|
||||
{
|
||||
case ParameterValueType::INT:
|
||||
paramValues[paramName] = ParameterValue{.Type = p.Type};
|
||||
break;
|
||||
|
||||
case ParameterValueType::STRING:
|
||||
paramValues[paramName] = ParameterValue{.Type = p.Type, .StringValue = value};
|
||||
break;
|
||||
|
||||
case ParameterValueType::INT_LIST:
|
||||
paramValues[paramName] = ParameterValue{.Type = p.Type};
|
||||
break;
|
||||
|
||||
case ParameterValueType::STRING_LIST:
|
||||
paramValues[paramName] = ParameterValue{.Type = p.Type};
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (argv[i][0] == '/')
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
printf(__CC_RED "Invalid command syntax, expected a parameter('-') or a switch('/')." __CC_DEFAULT
|
||||
"\n");
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
cmd.Action(IA_MOVE(paramValues), {});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
VOID CLI::DisplayHelp()
|
||||
{
|
||||
INT32 i{1};
|
||||
for (const auto &t : m_commands)
|
||||
{
|
||||
printf(__CC_WHITE "%i) " __CC_GREEN "%s (%c) " __CC_YELLOW "- %s\n", i, t->Value.ID.c_str(),
|
||||
t->Value.Shorthand, t->Value.Help.c_str());
|
||||
|
||||
for (const auto &v : t->Value.Parameters)
|
||||
{
|
||||
printf(__CC_WHITE "\t-%s %s %s\n", v.ID.c_str(), v.IsOptional ? "(optional)" : "", v.Help.c_str());
|
||||
}
|
||||
|
||||
for (const auto &v : t->Value.Switches)
|
||||
{
|
||||
printf(__CC_WHITE, "\t/%s %s\n", v.ID.c_str(), v.Help.c_str());
|
||||
}
|
||||
|
||||
printf(__CC_DEFAULT "\n");
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
VOID CLI::RegisterCommand(IN CONST CommandDesc &desc)
|
||||
{
|
||||
m_commands[desc.ID] = desc;
|
||||
}
|
||||
} // namespace ia
|
||||
@ -151,6 +151,7 @@
|
||||
|
||||
#define IA_VERSION_TYPE UINT64
|
||||
#define IA_MAKE_VERSION(major, minor, patch) ((static_cast<UINT64>(major) & 0xFFFFFF) << 40) | ((static_cast<UINT64>(minor) & 0xFFFFFF) << 16) | (static_cast<UINT64>(patch) & 0xFFFF)
|
||||
#define IA_STRINGIFY_VERSION(version) BuildString("v", (version >> 40) & 0xFFFFFF, ".", (version >> 16) & 0xFFFFFF, ".", version & 0xFFFF)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define IA_DLL_EXPORT __declspec(dllexport)
|
||||
|
||||
Reference in New Issue
Block a user