Files
test/source/blender/blenkernel/intern/blender_cli_command.cc
Sybren A. Stüvel 853640c063 Cleanup: clarify comment & variable in BKE_blender_cli_command_exec()
Just a little clarification. No functional changes.

Pull Request: https://projects.blender.org/blender/blender/pulls/136776
2025-04-01 16:30:01 +02:00

179 lines
5.0 KiB
C++

/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*
* Generic CLI "--command" declarations.
*
* Duplicate Commands
* ==================
*
* When two or more commands share the same identifier, a warning is printed and both are disabled.
*
* This is done because command-line actions may be destructive so the down-side of running the
* wrong command could be severe. The reason this is not considered an error is we can't prevent
* it so easily, unlike operator ID's which may be longer, commands are typically short terms
* which wont necessarily include an add-ons identifier as a prefix for e.g.
* Further, an error would break loading add-ons who's primary is *not*
* necessarily to provide command-line access.
* An alternative solution could be to generate unique names (number them for example)
* but this isn't reliable as it would depend on it the order add-ons are loaded which
* isn't under user control.
*/
#include <iostream>
#include "BLI_vector.hh"
#include "BKE_blender_cli_command.hh" /* own include */
/* -------------------------------------------------------------------- */
/** \name Internal API
* \{ */
using CommandHandlerPtr = std::unique_ptr<CommandHandler>;
/**
* All registered command handlers.
* \note the order doesn't matter as duplicates are detected and prevented from running.
*/
blender::Vector<CommandHandlerPtr> g_command_handlers;
static CommandHandler *blender_cli_command_lookup(const std::string &id)
{
for (CommandHandlerPtr &cmd_iter : g_command_handlers) {
if (id == cmd_iter->id) {
return cmd_iter.get();
}
}
return nullptr;
}
static int blender_cli_command_index(const CommandHandler *cmd)
{
int index = 0;
for (CommandHandlerPtr &cmd_iter : g_command_handlers) {
if (cmd_iter.get() == cmd) {
return index;
}
index++;
}
return -1;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Public API
* \{ */
void BKE_blender_cli_command_register(std::unique_ptr<CommandHandler> cmd)
{
bool is_duplicate = false;
if (CommandHandler *cmd_exists = blender_cli_command_lookup(cmd->id)) {
std::cerr << "warning: registered duplicate command \"" << cmd->id
<< "\", this will be inaccessible" << std::endl;
cmd_exists->is_duplicate = true;
is_duplicate = true;
}
cmd->is_duplicate = is_duplicate;
g_command_handlers.append(std::move(cmd));
}
bool BKE_blender_cli_command_unregister(CommandHandler *cmd)
{
const int cmd_index = blender_cli_command_index(cmd);
if (cmd_index == -1) {
std::cerr << "failed to unregister command handler" << std::endl;
return false;
}
/* Update duplicates after removal. */
if (cmd->is_duplicate) {
CommandHandler *cmd_other = nullptr;
for (CommandHandlerPtr &cmd_iter : g_command_handlers) {
/* Skip self. */
if (cmd == cmd_iter.get()) {
continue;
}
if (cmd_iter->is_duplicate && (cmd_iter->id == cmd->id)) {
if (cmd_other) {
/* Two or more found, clear and break. */
cmd_other = nullptr;
break;
}
cmd_other = cmd_iter.get();
}
}
if (cmd_other) {
cmd_other->is_duplicate = false;
}
}
g_command_handlers.remove_and_reorder(cmd_index);
return true;
}
int BKE_blender_cli_command_exec(bContext *C, const char *id, const int argc, const char **argv)
{
CommandHandler *cmd = blender_cli_command_lookup(id);
if (cmd == nullptr) {
std::cerr << "Unrecognized command: \"" << id << "\"" << std::endl;
return EXIT_FAILURE;
}
if (cmd->is_duplicate) {
std::cerr << "Command: \"" << id
<< "\" was registered multiple times, must be resolved, aborting!" << std::endl;
return EXIT_FAILURE;
}
return cmd->exec(C, argc, argv);
}
void BKE_blender_cli_command_print_help()
{
/* As `g_command_handlers` isn't ordered, sorting in-place is acceptable. */
std::sort(g_command_handlers.begin(),
g_command_handlers.end(),
[](const CommandHandlerPtr &a, const CommandHandlerPtr &b) { return a->id < b->id; });
for (int pass = 0; pass < 2; pass++) {
std::cout << ((pass == 0) ? "Blender Command Listing:" :
"Duplicate Command Listing (ignored):")
<< std::endl;
const bool show_duplicates = pass > 0;
bool found = false;
bool has_duplicate = false;
for (CommandHandlerPtr &cmd_iter : g_command_handlers) {
if (cmd_iter->is_duplicate) {
has_duplicate = true;
}
if (cmd_iter->is_duplicate != show_duplicates) {
continue;
}
std::cout << "\t" << cmd_iter->id << std::endl;
found = true;
}
if (!found) {
std::cout << "\tNone found" << std::endl;
}
/* Don't print that no duplicates are found as it's not helpful. */
if (pass == 0 && !has_duplicate) {
break;
}
}
}
void BKE_blender_cli_command_free_all()
{
g_command_handlers.clear();
}
/** \} */