console_commands: load() delega a parsers i buildHelp (cognitive 69→<25)

This commit is contained in:
2026-05-17 17:17:55 +02:00
parent 6fbd5988d4
commit 973bfa80bf
2 changed files with 101 additions and 99 deletions
+100 -99
View File
@@ -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; });
+1
View File
@@ -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())
};