diff --git a/cpp-generator/experimental/impl_header_parser.cpp b/cpp-generator/experimental/impl_header_parser.cpp index b872cb7..a61122a 100644 --- a/cpp-generator/experimental/impl_header_parser.cpp +++ b/cpp-generator/experimental/impl_header_parser.cpp @@ -76,6 +76,11 @@ static TypeExpr cppTypeToLidl(const QString& raw) if (t == "LogosList") return { TypeExpr::Array, "", { {TypeExpr::Primitive, "any", {}} } }; + // StdLogosResult — pure C++ result type for universal impls. The generator + // emits a StdLogosResult→Qt LogosResult conversion in the glue layer. + if (t == "StdLogosResult") + return { TypeExpr::Primitive, "result", {} }; + // Fallback: treat as opaque return { TypeExpr::Primitive, "any", {} }; } @@ -138,6 +143,9 @@ static bool parseMethodLine(const QString& line, MethodDecl& out) // Flag methods whose impl returns LogosMap / LogosList so the generator // can emit nlohmann→Qt conversion code in the glue layer. out.jsonReturn = (retTypeStr == "LogosMap" || retTypeStr == "LogosList"); + // Flag methods whose impl returns StdLogosResult so the generator can + // emit a StdLogosResult→Qt LogosResult conversion in the glue layer. + out.resultReturn = (retTypeStr == "StdLogosResult"); // Parse parameters out.params.clear(); diff --git a/cpp-generator/experimental/lidl_ast.h b/cpp-generator/experimental/lidl_ast.h index bd75f64..9fcb28b 100644 --- a/cpp-generator/experimental/lidl_ast.h +++ b/cpp-generator/experimental/lidl_ast.h @@ -43,10 +43,13 @@ struct MethodDecl { // True when the impl returns LogosMap or LogosList (nlohmann::json). // The generator will emit nlohmann→Qt conversion code in the glue layer. bool jsonReturn = false; + // True when the impl returns StdLogosResult. + // The generator will emit a stdResultToQt() conversion in the glue layer. + bool resultReturn = false; bool operator==(const MethodDecl& o) const { return name == o.name && params == o.params && returnType == o.returnType - && jsonReturn == o.jsonReturn; + && jsonReturn == o.jsonReturn && resultReturn == o.resultReturn; } }; diff --git a/cpp-generator/experimental/lidl_gen_provider.cpp b/cpp-generator/experimental/lidl_gen_provider.cpp index 465e6b5..fb5f30b 100644 --- a/cpp-generator/experimental/lidl_gen_provider.cpp +++ b/cpp-generator/experimental/lidl_gen_provider.cpp @@ -196,10 +196,12 @@ QString lidlMakeProviderHeader(const ModuleDecl& module, s << "} // anonymous namespace\n\n"; } - // Emit nlohmannToQVariant helper if any method returns LogosMap / LogosList + // Emit nlohmannToQVariant helper if any method returns LogosMap / LogosList / StdLogosResult bool needsNlohmannHelper = false; + bool needsResultHelper = false; for (const MethodDecl& md : module.methods) { - if (md.jsonReturn) { needsNlohmannHelper = true; break; } + if (md.jsonReturn) needsNlohmannHelper = true; + if (md.resultReturn) { needsNlohmannHelper = true; needsResultHelper = true; } } if (needsNlohmannHelper) { s << "#include \n\n"; @@ -226,6 +228,17 @@ QString lidlMakeProviderHeader(const ModuleDecl& module, s << " }\n"; s << " return QVariant();\n"; s << "}\n"; + if (needsResultHelper) { + s << "\n"; + s << "#include \"logos_result.h\"\n"; + s << "inline LogosResult stdResultToQt(const StdLogosResult& r) {\n"; + s << " LogosResult qr;\n"; + s << " qr.success = r.success;\n"; + s << " qr.value = nlohmannToQVariant(r.value);\n"; + s << " qr.error = r.error.empty() ? QVariant() : QVariant(QString::fromStdString(r.error));\n"; + s << " return qr;\n"; + s << "}\n"; + } s << "} // anonymous namespace\n\n"; } @@ -283,6 +296,15 @@ QString lidlMakeProviderHeader(const ModuleDecl& module, s << " return nlohmannToQVariant(_result).toMap();\n"; else s << " return nlohmannToQVariant(_result).toList();\n"; + } else if (md.resultReturn) { + // StdLogosResult: impl returns pure-C++ result, convert to Qt LogosResult + s << " auto _result = m_impl." << md.name << "("; + for (int i = 0; i < md.params.size(); ++i) { + s << qtParamToStd(md.params[i].type, md.params[i].name); + if (i + 1 < md.params.size()) s << ", "; + } + s << ");\n"; + s << " return stdResultToQt(_result);\n"; } else if (retConvertible) { s << " auto _result = m_impl." << md.name << "("; for (int i = 0; i < md.params.size(); ++i) { diff --git a/cpp/logos_result.h b/cpp/logos_result.h new file mode 100644 index 0000000..5a909be --- /dev/null +++ b/cpp/logos_result.h @@ -0,0 +1,19 @@ +#pragma once +#include +#include + +// Pure C++ result type for use in universal module implementations. +// No Qt dependency. The code generator recognizes "StdLogosResult" and emits +// a StdLogosResult -> Qt LogosResult conversion in the glue layer, so callers +// continue to receive the Qt LogosResult they expect. +// +// Usage in impl: +// StdLogosResult myMethod() { +// if (error) return {false, {}, "something went wrong"}; +// return {true, "some string value"}; // json accepts string, number, object, array +// } +struct StdLogosResult { + bool success = false; + nlohmann::json value; // any JSON value: string, number, bool, object, array, null + std::string error; +}; diff --git a/nix/include.nix b/nix/include.nix index b611001..3dc7b6f 100644 --- a/nix/include.nix +++ b/nix/include.nix @@ -36,7 +36,7 @@ pkgs.stdenv.mkDerivation { qt_provider_object.h qt_provider_object.cpp \ logos_transport.h logos_transport_factory.h logos_transport_factory.cpp \ logos_registry.h logos_registry_factory.h logos_registry_factory.cpp \ - plugin_registry.h logos_json.h; do + plugin_registry.h logos_json.h logos_result.h; do if [ -f cpp/$file ]; then cp cpp/$file $out/include/cpp/ fi diff --git a/tests/experimental/fixtures/universal_impl.h b/tests/experimental/fixtures/universal_impl.h index b29480c..7d46f33 100644 --- a/tests/experimental/fixtures/universal_impl.h +++ b/tests/experimental/fixtures/universal_impl.h @@ -3,6 +3,7 @@ #include // Parser tests only read this as text. LogosMap / LogosList match logos_json.h aliases. +// StdLogosResult matches logos_result.h. class UniversalImpl { public: UniversalImpl() = default; @@ -12,6 +13,7 @@ public: QVariantMap asVariantMap(); QStringList listNames(); QVariantList anyList(); + StdLogosResult fetchResult(); std::function emitEvent; }; diff --git a/tests/experimental/test_impl_header_parser.cpp b/tests/experimental/test_impl_header_parser.cpp index 04f83c5..7f4c48d 100644 --- a/tests/experimental/test_impl_header_parser.cpp +++ b/tests/experimental/test_impl_header_parser.cpp @@ -298,6 +298,13 @@ TEST_F(ImplHeaderParserTest, UniversalTypesAndMetadataEvents) EXPECT_EQ(anyList->returnType.elements[0].name, "any"); EXPECT_FALSE(anyList->jsonReturn); + auto fetchResult = findMethod("fetchResult"); + ASSERT_NE(fetchResult, nullptr); + EXPECT_EQ(fetchResult->returnType.kind, TypeExpr::Primitive); + EXPECT_EQ(fetchResult->returnType.name, "result"); + EXPECT_FALSE(fetchResult->jsonReturn); + EXPECT_TRUE(fetchResult->resultReturn); + for (const auto& m : r.module.methods) { EXPECT_NE(m.name, "void") << "Keyword should not appear as method name"; } diff --git a/tests/experimental/test_lidl_gen_provider.cpp b/tests/experimental/test_lidl_gen_provider.cpp index 63dba40..7994bed 100644 --- a/tests/experimental/test_lidl_gen_provider.cpp +++ b/tests/experimental/test_lidl_gen_provider.cpp @@ -326,6 +326,41 @@ TEST(LidlGenProvider, NoNlohmannBlockWithoutJsonReturnMethods) EXPECT_FALSE(h.contains("nlohmannToQVariant")); } +TEST(LidlGenProvider, HeaderIncludesNlohmannAndStdResultForResultReturn) +{ + ModuleDecl m; + m.name = "resmod"; + m.version = "1.0.0"; + MethodDecl md; + md.name = "getResult"; + md.returnType = { TypeExpr::Primitive, "result", {} }; + md.resultReturn = true; + m.methods.append(md); + + QString h = lidlMakeProviderHeader(m, "ResModImpl", "resmod_impl.h"); + EXPECT_TRUE(h.contains("#include ")); + EXPECT_TRUE(h.contains("nlohmannToQVariant")); + EXPECT_TRUE(h.contains("#include \"logos_result.h\"")); + EXPECT_TRUE(h.contains("stdResultToQt")); + EXPECT_TRUE(h.contains("auto _result = m_impl.getResult(")); + EXPECT_TRUE(h.contains("return stdResultToQt(_result)")); +} + +TEST(LidlGenProvider, StdResultMethodHasLogosResultReturnType) +{ + ModuleDecl m; + m.name = "resmod2"; + m.version = "1.0.0"; + MethodDecl md; + md.name = "doWork"; + md.returnType = { TypeExpr::Primitive, "result", {} }; + md.resultReturn = true; + m.methods.append(md); + + QString h = lidlMakeProviderHeader(m, "ResModImpl2", "resmod2_impl.h"); + EXPECT_TRUE(h.contains("LogosResult doWork(")); +} + // --------------------------------------------------------------------------- // Empty module // ---------------------------------------------------------------------------