From 8479c2323492874b48c4e76daace21d40cf4c936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=A7=CE=B3=CF=86=CF=84=20Kompanion?= Date: Wed, 15 Oct 2025 18:44:00 +0200 Subject: [PATCH] cli: drop exceptions from KompanionApp --- src/cli/KompanionApp.cpp | 162 ++++++++++++++++++------------ src/mcp/HandlersIntrospection.hpp | 10 +- 2 files changed, 108 insertions(+), 64 deletions(-) diff --git a/src/cli/KompanionApp.cpp b/src/cli/KompanionApp.cpp index 6a79650..c5aedbf 100644 --- a/src/cli/KompanionApp.cpp +++ b/src/cli/KompanionApp.cpp @@ -133,13 +133,17 @@ void writeDsnToConfig(const std::string& dsn) { #endif } -std::string readFileUtf8(const QString& path) { +bool readFileUtf8(const QString& path, std::string& out, QString* error) { QFile file(path); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - throw std::runtime_error(QString("Unable to open request file: %1").arg(path).toStdString()); + if (error) { + *error = QStringLiteral("Unable to open request file: %1").arg(path); + } + return false; } const QByteArray data = file.readAll(); - return QString::fromUtf8(data).toStdString(); + out = QString::fromUtf8(data).toStdString(); + return true; } bool looksLikeFile(const QString& value) { @@ -307,13 +311,20 @@ bool testConnection(const std::string& dsn, QString* error = nullptr) { return opened; } -bool schemaExists(QSqlDatabase& db) { +bool schemaExists(QSqlDatabase& db, bool* exists, QString* error) { QSqlQuery query(db); if (!query.exec(QStringLiteral("SELECT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname='public' AND tablename='memory_items')"))) { - throw std::runtime_error(query.lastError().text().toStdString()); + if (error) { + *error = query.lastError().text(); + } + return false; } - if (!query.next()) return false; - return query.value(0).toBool(); + if (!query.next()) { + if (exists) *exists = false; + return true; + } + if (exists) *exists = query.value(0).toBool(); + return true; } bool applySchemaFiles(QSqlDatabase& db, @@ -380,29 +391,48 @@ bool ensureSchema(const std::string& dsn, return false; } - bool success = false; - try { - if (schemaExists(db)) { - if (verbose) out << "Schema already present.\n"; - success = true; + bool exists = false; + QString err; + if (!schemaExists(db, &exists, &err)) { + out << "Failed to check schema: " << err << "\n"; + db.close(); + db = QSqlDatabase(); + QSqlDatabase::removeDatabase(connName); + return false; + } + + if (exists) { + if (verbose) out << "Schema already present.\n"; + db.close(); + db = QSqlDatabase(); + QSqlDatabase::removeDatabase(connName); + return true; + } + + out << "Schema not found; applying migrations...\n"; + if (!applySchemaFiles(db, out, verbose)) { + out << "Schema application reported errors.\n"; + db.close(); + db = QSqlDatabase(); + QSqlDatabase::removeDatabase(connName); + return false; + } + + if (!schemaExists(db, &exists, &err) || !exists) { + out << "Schema still missing after applying migrations.\n"; + if (!err.isEmpty()) { + out << "Last error: " << err << "\n"; } - if (!success) { - out << "Schema not found; applying migrations...\n"; - if (applySchemaFiles(db, out, verbose) && schemaExists(db)) { - out << "Schema initialized successfully.\n"; - success = true; - } else { - out << "Schema still missing after applying migrations.\n"; - success = false; - } - } - } catch (const std::exception& ex) { - out << "Failed to ensure schema: " << ex.what() << "\n"; + db.close(); + db = QSqlDatabase(); + QSqlDatabase::removeDatabase(connName); + return false; } db.close(); db = QSqlDatabase(); QSqlDatabase::removeDatabase(connName); - return success; + out << "Schema initialized successfully.\n"; + return true; } std::optional autoDetectDsn() { @@ -578,50 +608,54 @@ int runInteractiveSession(KomMcpServer& server, out.flush(); } - try { - const std::string response = server.dispatch(toolName, payload); - if (verbose) { - out << "[response] " << QString::fromStdString(response) << "\n"; - } else { - out << QString::fromStdString(response) << "\n"; - } - } catch (const std::exception& ex) { - out << "Error: " << ex.what() << "\n"; + const std::string response = server.dispatch(toolName, payload); + if (verbose) { + out << "[response] " << QString::fromStdString(response) << "\n"; + } else { + out << QString::fromStdString(response) << "\n"; } } return 0; } -std::string resolveRequestPayload(const QCommandLineParser& parser, - const QStringList& positional, - const QCommandLineOption& requestOption, - const QCommandLineOption& stdinOption) { +bool resolveRequestPayload(const QCommandLineParser& parser, + const QStringList& positional, + const QCommandLineOption& requestOption, + const QCommandLineOption& stdinOption, + std::string& payloadOut, + QString* error) { if (parser.isSet(stdinOption)) { - return readAll(std::cin); + payloadOut = readAll(std::cin); + return true; } if (parser.isSet(requestOption)) { const QString arg = parser.value(requestOption); if (arg == "-" || parser.isSet(stdinOption)) { - return readAll(std::cin); + payloadOut = readAll(std::cin); + return true; } if (looksLikeFile(arg)) { - return readFileUtf8(arg); + return readFileUtf8(arg, payloadOut, error); } - return arg.toStdString(); + payloadOut = arg.toStdString(); + return true; } if (positional.size() > 1) { const QString arg = positional.at(1); if (arg == "-") { - return readAll(std::cin); + payloadOut = readAll(std::cin); + return true; } if (looksLikeFile(arg)) { - return readFileUtf8(arg); + return readFileUtf8(arg, payloadOut, error); } - return arg.toStdString(); + payloadOut = arg.toStdString(); + return true; } - return "{}"; + payloadOut = "{}"; + return true; } void printToolList(const KomMcpServer& server) { @@ -749,25 +783,27 @@ int main(int argc, char** argv) { } std::string request; - try { - request = resolveRequestPayload(parser, positional, requestOption, stdinOption); - } catch (const std::exception& ex) { - std::cerr << "Error: " << ex.what() << "\n"; + QString requestError; + if (!resolveRequestPayload(parser, + positional, + requestOption, + stdinOption, + request, + &requestError)) { + const QString message = requestError.isEmpty() + ? QStringLiteral("Failed to resolve request payload.") + : requestError; + std::cerr << "Error: " << message.toStdString() << "\n"; return 1; } - try { - if (verbose) { - std::cerr << "[request] " << request << "\n"; - } - const std::string response = server.dispatch(toolName, request); - if (verbose) { - std::cerr << "[response] " << response << "\n"; - } - std::cout << response << std::endl; - return 0; - } catch (const std::exception& ex) { - std::cerr << "Error: " << ex.what() << "\n"; + if (verbose) { + std::cerr << "[request] " << request << "\n"; } - return 1; + const std::string response = server.dispatch(toolName, request); + if (verbose) { + std::cerr << "[response] " << response << "\n"; + } + std::cout << response << std::endl; + return 0; } diff --git a/src/mcp/HandlersIntrospection.hpp b/src/mcp/HandlersIntrospection.hpp index 3e41ae2..e5d02e3 100644 --- a/src/mcp/HandlersIntrospection.hpp +++ b/src/mcp/HandlersIntrospection.hpp @@ -74,7 +74,15 @@ inline std::string runCommandCapture(const char* cmd, std::size_t maxBytes = 819 (void)maxBytes; return "git status capture not supported on this platform"; #else - std::unique_ptr pipe(popen(cmd, "r"), pclose); + struct PipeCloser { + void operator()(FILE* file) const noexcept { + if (file != nullptr) { + pclose(file); + } + } + }; + + std::unique_ptr pipe(popen(cmd, "r"), PipeCloser{}); if (!pipe) { return "git status unavailable"; }