console_commands: load() delega a parsers i buildHelp (cognitive 69→<25)
This commit is contained in:
@@ -1103,7 +1103,78 @@ void CommandRegistry::registerHandlers() { // NOLINT(readability-function-cogni
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandRegistry::load(const std::string& yaml_path) { // NOLINT(readability-function-cognitive-complexity)
|
||||
namespace {
|
||||
// Parseja un node "scope" (string o sequence) a un vector. Buit si node és buit.
|
||||
auto parseScopeNode(const fkyaml::node& scope_node) -> std::vector<std::string> {
|
||||
std::vector<std::string> result;
|
||||
if (scope_node.is_sequence()) {
|
||||
std::ranges::transform(scope_node, std::back_inserter(result), [](const auto& s) { return s.template get_value<std::string>(); });
|
||||
} else {
|
||||
result.push_back(scope_node.get_value<std::string>());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Parseja un mapping de path → [options] en l'unordered_map de destí
|
||||
void parseCompletionsNode(const fkyaml::node& completions_node, std::unordered_map<std::string, std::vector<std::string>>& out) {
|
||||
for (auto it = completions_node.begin(); it != completions_node.end(); ++it) {
|
||||
auto path = it.key().get_value<std::string>();
|
||||
std::vector<std::string> opts;
|
||||
std::ranges::transform(*it, std::back_inserter(opts), [](const auto& opt) { return opt.template get_value<std::string>(); });
|
||||
out[path] = std::move(opts);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Aplica camps "debug_extras" sobre un CommandDef ja inicialitzat (només en _DEBUG)
|
||||
void applyDebugExtras(const fkyaml::node& extras, CommandDef& def) {
|
||||
if (extras.contains("description")) { def.description = extras["description"].get_value<std::string>(); }
|
||||
if (extras.contains("usage")) { def.usage = extras["usage"].get_value<std::string>(); }
|
||||
if (extras.contains("hidden")) { def.hidden = extras["hidden"].get_value<bool>(); }
|
||||
if (extras.contains("help_hidden")) { def.help_hidden = extras["help_hidden"].get_value<bool>(); }
|
||||
if (extras.contains("completions")) {
|
||||
def.completions.clear();
|
||||
parseCompletionsNode(extras["completions"], def.completions);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Parseja un cmd_node a CommandDef. Hereta de la categoria si el comand no defineix scope/debug_only.
|
||||
auto parseCommandDef(const fkyaml::node& cmd_node, const std::string& category, bool cat_debug_only, const std::vector<std::string>& cat_scopes) -> CommandDef {
|
||||
CommandDef def;
|
||||
def.keyword = cmd_node["keyword"].get_value<std::string>();
|
||||
def.handler_id = cmd_node["handler"].get_value<std::string>();
|
||||
def.category = category;
|
||||
def.description = cmd_node.contains("description") ? cmd_node["description"].get_value<std::string>() : "";
|
||||
def.usage = cmd_node.contains("usage") ? cmd_node["usage"].get_value<std::string>() : def.keyword;
|
||||
def.instant = cmd_node.contains("instant") && cmd_node["instant"].get_value<bool>();
|
||||
def.hidden = cmd_node.contains("hidden") && cmd_node["hidden"].get_value<bool>();
|
||||
def.debug_only = cat_debug_only || (cmd_node.contains("debug_only") && cmd_node["debug_only"].get_value<bool>());
|
||||
def.help_hidden = cmd_node.contains("help_hidden") && cmd_node["help_hidden"].get_value<bool>();
|
||||
def.dynamic_completions = cmd_node.contains("dynamic_completions") && cmd_node["dynamic_completions"].get_value<bool>();
|
||||
|
||||
if (cmd_node.contains("scope")) {
|
||||
def.scopes = parseScopeNode(cmd_node["scope"]);
|
||||
} else if (!cat_scopes.empty()) {
|
||||
def.scopes = cat_scopes;
|
||||
} else {
|
||||
def.scopes.emplace_back("global");
|
||||
}
|
||||
|
||||
if (cmd_node.contains("completions")) {
|
||||
parseCompletionsNode(cmd_node["completions"], def.completions);
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (cmd_node.contains("debug_extras")) {
|
||||
applyDebugExtras(cmd_node["debug_extras"], def);
|
||||
}
|
||||
#endif
|
||||
return def;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void CommandRegistry::load(const std::string& yaml_path) {
|
||||
registerHandlers();
|
||||
|
||||
// Cargar y parsear el YAML
|
||||
@@ -1127,115 +1198,21 @@ void CommandRegistry::load(const std::string& yaml_path) { // NOLINT(readabilit
|
||||
for (const auto& cat_node : yaml["categories"]) {
|
||||
const auto CATEGORY = cat_node["name"].get_value<std::string>();
|
||||
const bool CAT_DEBUG_ONLY = cat_node.contains("debug_only") && cat_node["debug_only"].get_value<bool>();
|
||||
|
||||
// Scopes por defecto de la categoría
|
||||
std::vector<std::string> cat_scopes;
|
||||
if (cat_node.contains("scope")) {
|
||||
const auto& scope_node = cat_node["scope"];
|
||||
if (scope_node.is_sequence()) {
|
||||
std::ranges::transform(scope_node, std::back_inserter(cat_scopes), [](const auto& s) { return s.template get_value<std::string>(); });
|
||||
} else {
|
||||
cat_scopes.push_back(scope_node.get_value<std::string>());
|
||||
}
|
||||
}
|
||||
const std::vector<std::string> CAT_SCOPES = cat_node.contains("scope") ? parseScopeNode(cat_node["scope"]) : std::vector<std::string>{};
|
||||
|
||||
if (!cat_node.contains("commands")) { continue; }
|
||||
|
||||
for (const auto& cmd_node : cat_node["commands"]) {
|
||||
CommandDef def;
|
||||
def.keyword = cmd_node["keyword"].get_value<std::string>();
|
||||
def.handler_id = cmd_node["handler"].get_value<std::string>();
|
||||
def.category = CATEGORY;
|
||||
def.description = cmd_node.contains("description") ? cmd_node["description"].get_value<std::string>() : "";
|
||||
def.usage = cmd_node.contains("usage") ? cmd_node["usage"].get_value<std::string>() : def.keyword;
|
||||
def.instant = cmd_node.contains("instant") && cmd_node["instant"].get_value<bool>();
|
||||
def.hidden = cmd_node.contains("hidden") && cmd_node["hidden"].get_value<bool>();
|
||||
def.debug_only = CAT_DEBUG_ONLY || (cmd_node.contains("debug_only") && cmd_node["debug_only"].get_value<bool>());
|
||||
def.help_hidden = cmd_node.contains("help_hidden") && cmd_node["help_hidden"].get_value<bool>();
|
||||
def.dynamic_completions = cmd_node.contains("dynamic_completions") && cmd_node["dynamic_completions"].get_value<bool>();
|
||||
|
||||
// Scopes: del comando, o hereda de la categoría, o "global" por defecto
|
||||
if (cmd_node.contains("scope")) {
|
||||
const auto& scope_node = cmd_node["scope"];
|
||||
if (scope_node.is_sequence()) {
|
||||
for (const auto& s : scope_node) { def.scopes.push_back(s.get_value<std::string>()); }
|
||||
} else {
|
||||
def.scopes.push_back(scope_node.get_value<std::string>());
|
||||
}
|
||||
} else if (!cat_scopes.empty()) {
|
||||
def.scopes = cat_scopes;
|
||||
} else {
|
||||
def.scopes.emplace_back("global");
|
||||
}
|
||||
|
||||
// Completions estáticas
|
||||
if (cmd_node.contains("completions")) {
|
||||
auto completions_node = cmd_node["completions"];
|
||||
for (auto it = completions_node.begin(); it != completions_node.end(); ++it) {
|
||||
auto path = it.key().get_value<std::string>();
|
||||
std::vector<std::string> opts;
|
||||
std::ranges::transform(*it, std::back_inserter(opts), [](const auto& opt) { return opt.template get_value<std::string>(); });
|
||||
def.completions[path] = std::move(opts);
|
||||
}
|
||||
}
|
||||
|
||||
// Aplicar debug_extras en debug builds
|
||||
#ifdef _DEBUG
|
||||
if (cmd_node.contains("debug_extras")) {
|
||||
const auto& extras = cmd_node["debug_extras"];
|
||||
if (extras.contains("description")) { def.description = extras["description"].get_value<std::string>(); }
|
||||
if (extras.contains("usage")) { def.usage = extras["usage"].get_value<std::string>(); }
|
||||
if (extras.contains("hidden")) { def.hidden = extras["hidden"].get_value<bool>(); }
|
||||
if (extras.contains("help_hidden")) { def.help_hidden = extras["help_hidden"].get_value<bool>(); }
|
||||
if (extras.contains("completions")) {
|
||||
def.completions.clear();
|
||||
auto extras_completions = extras["completions"];
|
||||
for (auto it = extras_completions.begin(); it != extras_completions.end(); ++it) {
|
||||
auto path = it.key().get_value<std::string>();
|
||||
std::vector<std::string> opts;
|
||||
std::ranges::transform(*it, std::back_inserter(opts), [](const auto& opt) { return opt.template get_value<std::string>(); });
|
||||
def.completions[path] = std::move(opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// En Release: saltar comandos debug_only
|
||||
CommandDef def = parseCommandDef(cmd_node, CATEGORY, CAT_DEBUG_ONLY, CAT_SCOPES);
|
||||
#ifndef _DEBUG
|
||||
if (def.debug_only) { continue; }
|
||||
#endif
|
||||
|
||||
commands_.push_back(std::move(def));
|
||||
}
|
||||
}
|
||||
|
||||
// Registrar el handler de HELP (captura this)
|
||||
handlers_["cmd_help"] = [this](const std::vector<std::string>& args) -> std::string {
|
||||
if (!args.empty()) {
|
||||
// HELP <command>: mostrar ayuda detallada de un comando
|
||||
const auto* cmd = findCommand(args[0]);
|
||||
if (cmd != nullptr) {
|
||||
std::string kw_lower = cmd->keyword;
|
||||
std::ranges::transform(kw_lower, kw_lower.begin(), ::tolower);
|
||||
std::string result = kw_lower + ": " + cmd->description + "\n" + cmd->usage;
|
||||
|
||||
// Listar subcomandos/opciones si hay completions
|
||||
auto opts = getCompletions(cmd->keyword);
|
||||
if (!opts.empty()) {
|
||||
result += "\noptions:";
|
||||
for (const auto& opt : opts) {
|
||||
std::string opt_lower = opt;
|
||||
std::ranges::transform(opt_lower, opt_lower.begin(), ::tolower);
|
||||
result += " " + opt_lower;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return "Unknown command: " + args[0];
|
||||
}
|
||||
std::cout << generateTerminalHelp();
|
||||
return generateConsoleHelp();
|
||||
};
|
||||
// Registrar el handler de HELP (delega a buildHelp per mantenir baixa la complexitat de load)
|
||||
handlers_["cmd_help"] = [this](const std::vector<std::string>& args) -> std::string { return buildHelp(args); };
|
||||
|
||||
// Aplanar completions en el mapa global
|
||||
for (const auto& cmd : commands_) {
|
||||
@@ -1245,6 +1222,30 @@ void CommandRegistry::load(const std::string& yaml_path) { // NOLINT(readabilit
|
||||
}
|
||||
}
|
||||
|
||||
auto CommandRegistry::buildHelp(const std::vector<std::string>& args) const -> std::string {
|
||||
if (args.empty()) {
|
||||
std::cout << generateTerminalHelp();
|
||||
return generateConsoleHelp();
|
||||
}
|
||||
// HELP <command>: ajuda detallada
|
||||
const auto* cmd = findCommand(args[0]);
|
||||
if (cmd == nullptr) { return "Unknown command: " + args[0]; }
|
||||
|
||||
std::string kw_lower = cmd->keyword;
|
||||
std::ranges::transform(kw_lower, kw_lower.begin(), ::tolower);
|
||||
std::string result = kw_lower + ": " + cmd->description + "\n" + cmd->usage;
|
||||
|
||||
const auto OPTS = getCompletions(cmd->keyword);
|
||||
if (OPTS.empty()) { return result; }
|
||||
result += "\noptions:";
|
||||
for (const auto& opt : OPTS) {
|
||||
std::string opt_lower = opt;
|
||||
std::ranges::transform(opt_lower, opt_lower.begin(), ::tolower);
|
||||
result += " " + opt_lower;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto CommandRegistry::findCommand(const std::string& keyword) const -> const CommandDef* {
|
||||
auto it = std::ranges::find_if(commands_,
|
||||
[&keyword](const auto& cmd) { return cmd.keyword == keyword; });
|
||||
|
||||
@@ -58,4 +58,5 @@ class CommandRegistry {
|
||||
|
||||
void registerHandlers();
|
||||
[[nodiscard]] auto isCommandVisible(const CommandDef& cmd) const -> bool;
|
||||
[[nodiscard]] auto buildHelp(const std::vector<std::string>& args) const -> std::string; // Cos del handler HELP (extret per reduir complexitat de load())
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user