// This file was generated with the assistance of an AI coding tool. /******************************************************************************** * * * This file is part of Bonsai. * * * * Bonsai is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3.0 of the License, or * * (at your option) any later version. * * * * Bonsai is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ********************************************************************************/ #include "Discovery.h" #include #include #include #include #include #include #include #include namespace bonsaiviewer::modules::connectors { namespace { QString resolveExec(const QString& exec_field, const QString& folder) { QString resolved = QDir(folder).absoluteFilePath(exec_field); #if defined(Q_OS_WIN) if (!QFileInfo::exists(resolved) && QFileInfo::exists(resolved + ".exe")) { resolved += ".exe"; } #endif return QDir::cleanPath(resolved); } bool parseManifest(const QString& folder, ConnectorManifest& out) { const QString manifest_path = QDir(folder).filePath("connector.json"); QFile file(manifest_path); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "ifcviewer connectors: cannot read" << manifest_path << ":" << file.errorString(); return false; } QJsonParseError err{}; const QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &err); if (doc.isNull() || !doc.isObject()) { qWarning() << "ifcviewer connectors: malformed manifest" << manifest_path << ":" << err.errorString(); return false; } const QJsonObject obj = doc.object(); const QString id = obj.value("id").toString(); const QString name = obj.value("name").toString(); const QString version = obj.value("version").toString(); const QString exec_field = obj.value("exec").toString(); if (id.isEmpty() || name.isEmpty() || exec_field.isEmpty()) { qWarning() << "ifcviewer connectors: missing required field (id/name/exec) in" << manifest_path; return false; } out.id = id; out.name = name; out.version = version; out.folder = QDir::cleanPath(folder); out.exec_path = resolveExec(exec_field, out.folder); return true; } } // namespace QString bundledConnectorsDir() { // Connectors ship alongside the executable in a "connectors" subdirectory. return QDir(QCoreApplication::applicationDirPath()).filePath("connectors"); } std::vector discoverConnectors() { std::vector result; QSet seen_ids; QDir dir(bundledConnectorsDir()); if (!dir.exists()) return result; const QFileInfoList entries = dir.entryInfoList( QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name); for (const QFileInfo& entry : entries) { if (!QFileInfo(QDir(entry.absoluteFilePath()).filePath("connector.json")).exists()) continue; ConnectorManifest m; if (!parseManifest(entry.absoluteFilePath(), m)) continue; if (seen_ids.contains(m.id)) { qWarning() << "ifcviewer connectors: duplicate id" << m.id << "at" << entry.absoluteFilePath() << "ignored (earlier match wins)"; continue; } seen_ids.insert(m.id); result.push_back(std::move(m)); } return result; } } // namespace bonsaiviewer::modules::connectors