This commit is contained in:
Isuru Samarathunga
2025-10-10 13:30:08 +05:30
parent af084e0b94
commit 5c02010ae1
2 changed files with 219 additions and 0 deletions

View 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> &&parameters, IN Map<String, BOOL> &&switchValues)>
Action;
};
public:
CLI(IN CONST String &appName, IN IA_VERSION_TYPE appVersion, IN CONST String &copyrightNotice)
: 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> &&parameters, 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

View File

@ -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)