From c38f507214221bd69008656db57611336ef71d46 Mon Sep 17 00:00:00 2001 From: Dario Gabriel Lipicar Date: Fri, 24 Apr 2026 16:21:04 -0300 Subject: [PATCH] fix LogosResult --- .../plain/qvariant_rpc_value.cpp | 33 +++++++++++++++++++ cpp/logos_types.cpp | 9 +++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/cpp/implementations/plain/qvariant_rpc_value.cpp b/cpp/implementations/plain/qvariant_rpc_value.cpp index ca1c23d..8761a6b 100644 --- a/cpp/implementations/plain/qvariant_rpc_value.cpp +++ b/cpp/implementations/plain/qvariant_rpc_value.cpp @@ -1,5 +1,9 @@ #include "qvariant_rpc_value.h" +#include "../../logos_types.h" + +#include + namespace logos::plain { namespace { @@ -75,6 +79,35 @@ RpcValue qvariantToRpcValue(const QVariant& v) { if (!v.isValid()) return RpcValue{std::monostate{}}; + // LogosResult is a user-defined struct registered via qRegisterMetaType; + // its metatype id is assigned at runtime so we can't put it in the + // switch on QMetaType::Type below. Check it first — if we let it fall + // through to the default, we'd stringify it via QVariant::toString() + // (returning "" because LogosResult has no QString converter) or, + // earlier, lose it as std::monostate{} and the receiver would see null. + // + // Wire shape: {"success": bool, "value": , "error": }. + // That matches the struct's fields and recursively reuses the RpcValue + // conversion for `value` and `error`, which themselves are QVariants + // carrying primitives / QVariantMap / QVariantList / etc. + // + // Look up the metatype id per call (not cached in a `static`): the + // first `qvariantToRpcValue` call might land before any `LogosAPI` + // has called `qRegisterMetaType`, and we don't want to + // permanently cache `UnknownType` in that case. The lookup is a + // hash probe — trivially cheap compared to the actual RPC work. + { + const int logosResultId = QMetaType::fromName("LogosResult").id(); + if (logosResultId != QMetaType::UnknownType && v.userType() == logosResultId) { + const LogosResult r = v.value(); + RpcMap m; + m.emplace("success", RpcValue{r.success}); + m.emplace("value", qvariantToRpcValue(r.value)); + m.emplace("error", qvariantToRpcValue(r.error)); + return RpcValue{std::move(m)}; + } + } + // Fast path for the common scalar types. switch (static_cast(v.userType())) { case QMetaType::Bool: return RpcValue{v.toBool()}; diff --git a/cpp/logos_types.cpp b/cpp/logos_types.cpp index 71fe721..56a08bc 100644 --- a/cpp/logos_types.cpp +++ b/cpp/logos_types.cpp @@ -1,11 +1,16 @@ #include "logos_types.h" +// All three fields. The `error` field used to be dropped on the wire — +// senders set it, receivers got a default-constructed (null) QVariant — +// so any failed LogosResult looked like a success path with no explanation. +// Daemon and modules always build from the same SDK (they ship together in +// the same process group) so extending the wire format is safe. QDataStream& operator<<(QDataStream& out, const LogosResult& result) { - out << result.success << result.value; + out << result.success << result.value << result.error; return out; } QDataStream& operator>>(QDataStream& in, LogosResult& result) { - in >> result.success >> result.value; + in >> result.success >> result.value >> result.error; return in; } \ No newline at end of file