diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 71cbf35..4c41e52 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -38,6 +38,8 @@ set(SDK_SOURCES logos_object.h logos_provider_object.cpp logos_provider_object.h + logos_json_convert.cpp + logos_json_convert.h logos_module_context.h qt_provider_object.cpp qt_provider_object.h @@ -185,6 +187,7 @@ install(FILES plugin_registry.h logos_object.h logos_provider_object.h + logos_json_convert.h logos_module_context.h qt_provider_object.h logos_transport.h diff --git a/cpp/logos_api_client.cpp b/cpp/logos_api_client.cpp index c012b7d..5719d0d 100644 --- a/cpp/logos_api_client.cpp +++ b/cpp/logos_api_client.cpp @@ -3,6 +3,7 @@ #include "logos_api_consumer.h" #include "logos_object.h" #include "logos_types.h" +#include "logos_json_convert.h" #include "token_manager.h" #include #include @@ -13,89 +14,8 @@ #include #include -// --------------------------------------------------------------------------- -// Internal helpers: convert between nlohmann::json and Qt types -// --------------------------------------------------------------------------- - -namespace { - -nlohmann::json qvariantToNlohmann(const QVariant& v) -{ - // LogosResult registered metatype - const int logosResultId = QMetaType::fromName("LogosResult").id(); - if (logosResultId != QMetaType::UnknownType && v.userType() == logosResultId) { - const LogosResult lr = v.value(); - nlohmann::json obj; - obj["success"] = lr.success; - QJsonValue valJson = QJsonValue::fromVariant(lr.value); - if (valJson.isObject() || valJson.isArray()) { - QJsonDocument d = valJson.isObject() ? QJsonDocument(valJson.toObject()) - : QJsonDocument(valJson.toArray()); - try { obj["value"] = nlohmann::json::parse(d.toJson(QJsonDocument::Compact).toStdString()); } - catch (...) { obj["value"] = nullptr; } - } else if (valJson.isString()) obj["value"] = valJson.toString().toStdString(); - else if (valJson.isBool()) obj["value"] = valJson.toBool(); - else if (valJson.isDouble()) obj["value"] = valJson.toDouble(); - else obj["value"] = nullptr; - QJsonValue errJson = QJsonValue::fromVariant(lr.error); - obj["error"] = errJson.isString() ? nlohmann::json(errJson.toString().toStdString()) - : nullptr; - return obj; - } - - if (v.canConvert()) { - QJsonDocument doc(v.toJsonObject()); - try { return nlohmann::json::parse(doc.toJson(QJsonDocument::Compact).toStdString()); } - catch (...) {} - } - if (v.canConvert()) { - QJsonDocument doc(qvariant_cast(v)); - try { return nlohmann::json::parse(doc.toJson(QJsonDocument::Compact).toStdString()); } - catch (...) {} - } - - QJsonValue jv = QJsonValue::fromVariant(v); - if (jv.isString()) return jv.toString().toStdString(); - if (jv.isBool()) return jv.toBool(); - if (jv.isDouble()) return jv.toDouble(); - if (jv.isObject() || jv.isArray()) { - QJsonDocument doc = jv.isObject() ? QJsonDocument(jv.toObject()) - : QJsonDocument(jv.toArray()); - try { return nlohmann::json::parse(doc.toJson(QJsonDocument::Compact).toStdString()); } - catch (...) {} - } - return nullptr; -} - -QVariantList nlohmannArgsToQVariantList(const nlohmann::json& args) -{ - QVariantList result; - if (!args.is_array()) return result; - for (const auto& arg : args) { - if (arg.is_string()) - result.append(QString::fromStdString(arg.get())); - else if (arg.is_boolean()) - result.append(arg.get()); - else if (arg.is_number_integer()) - result.append(static_cast(arg.get())); - else if (arg.is_number_float()) - result.append(arg.get()); - else if (arg.is_null()) - result.append(QVariant()); - else if (arg.is_object() || arg.is_array()) { - QJsonDocument doc = QJsonDocument::fromJson( - QByteArray::fromStdString(arg.dump())); - result.append(arg.is_object() - ? QVariant::fromValue(doc.object()) - : QVariant::fromValue(doc.array())); - } else { - result.append(QVariant()); - } - } - return result; -} - -} // namespace +using logos::qvariantToNlohmann; +using logos::nlohmannArgsToQVariantList; LogosAPIClient::LogosAPIClient(const QString& module_to_talk_to, const QString& origin_module, diff --git a/cpp/logos_json_convert.cpp b/cpp/logos_json_convert.cpp new file mode 100644 index 0000000..f8f3d4a --- /dev/null +++ b/cpp/logos_json_convert.cpp @@ -0,0 +1,136 @@ +#include "logos_json_convert.h" +#include "logos_types.h" +#include +#include +#include +#include +#include + +namespace logos { + +nlohmann::json qvariantToNlohmann(const QVariant& v) +{ + const int logosResultId = QMetaType::fromName("LogosResult").id(); + if (logosResultId != QMetaType::UnknownType && v.userType() == logosResultId) { + const LogosResult lr = v.value(); + nlohmann::json obj; + obj["success"] = lr.success; + QJsonValue valJson = QJsonValue::fromVariant(lr.value); + if (valJson.isObject() || valJson.isArray()) { + QJsonDocument d = valJson.isObject() ? QJsonDocument(valJson.toObject()) + : QJsonDocument(valJson.toArray()); + try { obj["value"] = nlohmann::json::parse(d.toJson(QJsonDocument::Compact).toStdString()); } + catch (...) { obj["value"] = nullptr; } + } else if (valJson.isString()) obj["value"] = valJson.toString().toStdString(); + else if (valJson.isBool()) obj["value"] = valJson.toBool(); + else if (valJson.isDouble()) obj["value"] = valJson.toDouble(); + else obj["value"] = nullptr; + QJsonValue errJson = QJsonValue::fromVariant(lr.error); + obj["error"] = errJson.isString() ? nlohmann::json(errJson.toString().toStdString()) + : nullptr; + return obj; + } + + if (v.canConvert()) { + QJsonDocument doc(v.toJsonObject()); + try { return nlohmann::json::parse(doc.toJson(QJsonDocument::Compact).toStdString()); } + catch (...) {} + } + if (v.canConvert()) { + QJsonDocument doc(qvariant_cast(v)); + try { return nlohmann::json::parse(doc.toJson(QJsonDocument::Compact).toStdString()); } + catch (...) {} + } + + QJsonValue jv = QJsonValue::fromVariant(v); + if (jv.isString()) return jv.toString().toStdString(); + if (jv.isBool()) return jv.toBool(); + if (jv.isDouble()) return jv.toDouble(); + if (jv.isObject() || jv.isArray()) { + QJsonDocument doc = jv.isObject() ? QJsonDocument(jv.toObject()) + : QJsonDocument(jv.toArray()); + try { return nlohmann::json::parse(doc.toJson(QJsonDocument::Compact).toStdString()); } + catch (...) {} + } + return nullptr; +} + +QVariant nlohmannToQVariant(const nlohmann::json& j) +{ + if (j.is_null()) + return QVariant(); + if (j.is_boolean()) + return QVariant(j.get()); + if (j.is_number_unsigned()) + return QVariant(static_cast(j.get())); + if (j.is_number_integer()) + return QVariant(static_cast(j.get())); + if (j.is_number_float()) + return QVariant(j.get()); + if (j.is_string()) + return QVariant(QString::fromStdString(j.get())); + if (j.is_object()) { + QJsonDocument doc = QJsonDocument::fromJson( + QByteArray::fromStdString(j.dump())); + return QVariant::fromValue(doc.object()); + } + if (j.is_array()) { + QJsonDocument doc = QJsonDocument::fromJson( + QByteArray::fromStdString(j.dump())); + return QVariant::fromValue(doc.array()); + } + return QVariant(); +} + +QVariantList nlohmannArgsToQVariantList(const nlohmann::json& args) +{ + QVariantList result; + if (!args.is_array()) return result; + for (const auto& arg : args) { + if (arg.is_string()) + result.append(QString::fromStdString(arg.get())); + else if (arg.is_boolean()) + result.append(arg.get()); + else if (arg.is_number_unsigned()) + result.append(static_cast(arg.get())); + else if (arg.is_number_integer()) + result.append(static_cast(arg.get())); + else if (arg.is_number_float()) + result.append(arg.get()); + else if (arg.is_null()) + result.append(QVariant()); + else if (arg.is_object() || arg.is_array()) { + QJsonDocument doc = QJsonDocument::fromJson( + QByteArray::fromStdString(arg.dump())); + result.append(arg.is_object() + ? QVariant::fromValue(doc.object()) + : QVariant::fromValue(doc.array())); + } else { + result.append(QVariant()); + } + } + return result; +} + +QJsonArray methodsToJsonArray(const std::vector& methods) +{ + QJsonArray out; + for (const auto& m : methods) { + QJsonObject o; + o["name"] = QString::fromStdString(m.name); + o["signature"] = QString::fromStdString(m.signature); + o["returnType"] = QString::fromStdString(m.returnType); + o["isInvokable"] = m.isInvokable; + if (m.parameters.is_array()) { + QJsonDocument paramDoc = QJsonDocument::fromJson( + QByteArray::fromStdString(m.parameters.dump())); + o["parameters"] = paramDoc.array(); + } else { + o["parameters"] = QJsonArray(); + } + out.append(o); + } + return out; +} + +} // namespace logos diff --git a/cpp/logos_json_convert.h b/cpp/logos_json_convert.h new file mode 100644 index 0000000..813d75c --- /dev/null +++ b/cpp/logos_json_convert.h @@ -0,0 +1,28 @@ +#ifndef LOGOS_JSON_CONVERT_H +#define LOGOS_JSON_CONVERT_H + +#include +#include +#include +#include +#include +#include + +struct LogosMethodMetadata { + std::string name; + std::string signature; + std::string returnType; + bool isInvokable = true; + nlohmann::json parameters = nlohmann::json::array(); +}; + +namespace logos { + +nlohmann::json qvariantToNlohmann(const QVariant& v); +QVariant nlohmannToQVariant(const nlohmann::json& j); +QVariantList nlohmannArgsToQVariantList(const nlohmann::json& args); +QJsonArray methodsToJsonArray(const std::vector& methods); + +} // namespace logos + +#endif // LOGOS_JSON_CONVERT_H diff --git a/cpp/logos_provider_object.cpp b/cpp/logos_provider_object.cpp index d8b18b5..8051406 100644 --- a/cpp/logos_provider_object.cpp +++ b/cpp/logos_provider_object.cpp @@ -1,7 +1,68 @@ #include "logos_provider_object.h" +#include "logos_json_convert.h" #include "logos_api.h" #include "token_manager.h" #include +#include + +// --------------------------------------------------------------------------- +// LogosProviderObject — universal virtual defaults +// --------------------------------------------------------------------------- + +nlohmann::json LogosProviderObject::callMethodStd(const std::string& /*methodName*/, + const nlohmann::json& /*args*/) +{ + return nullptr; +} + +std::vector LogosProviderObject::getMethodsStd() +{ + return {}; +} + +void LogosProviderObject::setEventListenerStd(UniversalEventCallback /*callback*/) +{ +} + +// --------------------------------------------------------------------------- +// LogosProviderObject — bridge helpers (Qt-free providers delegate here) +// --------------------------------------------------------------------------- + +QVariant LogosProviderObject::callMethodStdBridge(const QString& methodName, const QVariantList& args) +{ + nlohmann::json jArgs = nlohmann::json::array(); + for (const QVariant& a : args) + jArgs.push_back(logos::qvariantToNlohmann(a)); + + nlohmann::json result = callMethodStd(methodName.toStdString(), jArgs); + return logos::nlohmannToQVariant(result); +} + +QJsonArray LogosProviderObject::getMethodsStdBridge() +{ + return logos::methodsToJsonArray(getMethodsStd()); +} + +void LogosProviderObject::setEventListenerStdBridge(EventCallback callback) +{ + setEventListenerStd([callback](const std::string& eventName, const std::string& data) { + if (!callback) return; + QVariantList qData; + QJsonDocument doc = QJsonDocument::fromJson( + QByteArray::fromStdString(data)); + if (doc.isArray()) { + for (const QJsonValue& v : doc.array()) + qData.append(v.toVariant()); + } else { + qData.append(QString::fromStdString(data)); + } + callback(QString::fromStdString(eventName), qData); + }); +} + +// --------------------------------------------------------------------------- +// LogosProviderBase +// --------------------------------------------------------------------------- void LogosProviderBase::init(void* apiInstance) { diff --git a/cpp/logos_provider_object.h b/cpp/logos_provider_object.h index 4a5f96f..44f8ce8 100644 --- a/cpp/logos_provider_object.h +++ b/cpp/logos_provider_object.h @@ -5,7 +5,12 @@ #include #include #include +#include #include +#include +#include + +#include "logos_json_convert.h" class LogosAPI; @@ -15,13 +20,23 @@ class LogosAPI; // This is the provider-side counterpart of LogosObject (consumer side). // ModuleProxy wraps a LogosProviderObject* and publishes it via the transport. // Module authors do NOT implement this directly — they inherit LogosProviderBase. +// +// Two parallel virtual interfaces: +// Qt interface: callMethod / getMethods / setEventListener (pure virtual) +// Universal interface: callMethodStd / getMethodsStd / setEventListenerStd (defaulted) +// +// Providers override ONE set. Those going Qt-free override the Std versions +// and delegate the Qt ones via the provided callMethodStdBridge / getMethodsStdBridge +// helpers (one-line overrides). // --------------------------------------------------------------------------- class LogosProviderObject { public: virtual ~LogosProviderObject() = default; using EventCallback = std::function; + using UniversalEventCallback = std::function; + // --- Qt interface (pure virtual — existing providers override these) --- virtual QVariant callMethod(const QString& methodName, const QVariantList& args) = 0; virtual bool informModuleToken(const QString& moduleName, const QString& token) = 0; virtual QJsonArray getMethods() = 0; @@ -29,6 +44,18 @@ public: virtual void init(void* apiInstance) = 0; virtual QString providerName() const = 0; virtual QString providerVersion() const = 0; + + // --- Universal interface (override these to stay Qt-free) --- + virtual nlohmann::json callMethodStd(const std::string& methodName, const nlohmann::json& args); + virtual std::vector getMethodsStd(); + virtual void setEventListenerStd(UniversalEventCallback callback); + +protected: + // Bridging helpers for Qt-free providers: override callMethod/getMethods + // with a one-liner delegating to these. + QVariant callMethodStdBridge(const QString& methodName, const QVariantList& args); + QJsonArray getMethodsStdBridge(); + void setEventListenerStdBridge(EventCallback callback); }; // --------------------------------------------------------------------------- diff --git a/nix/include.nix b/nix/include.nix index 43dce32..5f44dba 100644 --- a/nix/include.nix +++ b/nix/include.nix @@ -34,6 +34,7 @@ pkgs.stdenv.mkDerivation { token_manager.cpp token_manager.h module_proxy.cpp module_proxy.h logos_mode.h \ logos_instance.h logos_object.h \ logos_provider_object.h logos_provider_object.cpp \ + logos_json_convert.h logos_json_convert.cpp \ qt_provider_object.h qt_provider_object.cpp \ logos_transport.h logos_transport.cpp logos_transport_config.h \ logos_transport_factory.h logos_transport_factory.cpp \