* @brief Execute a registered command */
| 198 | * @brief Execute a registered command |
| 199 | */ |
| 200 | API::CommandResponse API::CommandRegistry::execute(const QString& name, |
| 201 | const QString& id, |
| 202 | const QJsonObject& params) |
| 203 | { |
| 204 | if (!hasCommand(name)) |
| 205 | return buildUnknownCommandResponse(name, id); |
| 206 | |
| 207 | const bool isDryRun = params.value(QStringLiteral("dryRun")).toBool(false); |
| 208 | QString preMutationBackup; |
| 209 | if (!isDryRun && destructiveCommandSet().contains(name)) |
| 210 | preMutationBackup = Misc::BackupManager::instance().snapshot(QStringLiteral("pre-") + name); |
| 211 | |
| 212 | const qint64 epochBefore = DataModel::ProjectModel::instance().mutationEpoch(); |
| 213 | |
| 214 | try { |
| 215 | auto response = m_commands[name].handler(id, params); |
| 216 | attachErrorMetadata(name, response); |
| 217 | |
| 218 | if (!isDryRun && response.success |
| 219 | && DataModel::ProjectModel::instance().mutationEpoch() != epochBefore) { |
| 220 | DataModel::ProjectModel::instance().scheduleAutoSave(); |
| 221 | scheduleProjectApply(); |
| 222 | } |
| 223 | |
| 224 | if (!preMutationBackup.isEmpty() && response.success |
| 225 | && !response.result.contains(QStringLiteral("backupPath"))) { |
| 226 | auto result = response.result; |
| 227 | result[QStringLiteral("backupPath")] = preMutationBackup; |
| 228 | response.result = result; |
| 229 | } |
| 230 | |
| 231 | return response; |
| 232 | } catch (const std::exception& e) { |
| 233 | return CommandResponse::makeError( |
| 234 | id, ErrorCode::ExecutionError, QStringLiteral("Command execution failed: %1").arg(e.what())); |
| 235 | } catch (...) { |
| 236 | return CommandResponse::makeError( |
| 237 | id, ErrorCode::ExecutionError, QStringLiteral("Command execution failed: unknown exception")); |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | /** |
| 242 | * @brief Builds a "Unknown command" error response with did_you_mean suggestions. |