#ifndef LOGOS_PLAIN_RPC_VALUE_H #define LOGOS_PLAIN_RPC_VALUE_H #include #include #include #include #include #include #include namespace logos::plain { // ----------------------------------------------------------------------------- // RpcValue — plain C++ variant carried by the wire RPC layer. // // Covers the shapes we actually need (null / bool / int / double / string / // bytes / list / map). No Qt types. The JSON/CBOR codec converts to/from // `nlohmann::json`; Qt-side callers convert to/from `QVariant` at the Qt // boundary (see plain_logos_object.cpp, plain_transport_host.cpp). // // Uses recursive std::variant via wrapper structs so list/map can hold // RpcValue children without forward-declaration headaches. // ----------------------------------------------------------------------------- struct RpcValue; struct RpcList { std::vector items; bool operator==(const RpcList& other) const { return items == other.items; } bool operator!=(const RpcList& other) const { return !(*this == other); } }; // std::map would require RpcValue to be complete at this // point, which is impossible (RpcValue contains RpcMap as a variant alt). // Use a vector of pairs instead — also gives us deterministic encoding // order for free, which matters when we move to CBOR. // // Method bodies that dereference RpcValue are defined out-of-line below, // once RpcValue is complete. struct RpcMap { std::vector> entries; void emplace(std::string key, RpcValue val); const RpcValue* find(const std::string& key) const; const RpcValue& at(const std::string& key) const; bool operator==(const RpcMap& other) const; bool operator!=(const RpcMap& other) const { return !(*this == other); } }; struct RpcBytes { std::vector data; bool operator==(const RpcBytes& other) const { return data == other.data; } bool operator!=(const RpcBytes& other) const { return !(*this == other); } }; struct RpcValue { using Variant = std::variant< std::monostate, // null bool, int64_t, double, std::string, RpcBytes, RpcList, RpcMap >; Variant value; RpcValue() = default; RpcValue(std::monostate) : value(std::monostate{}) {} RpcValue(bool b) : value(b) {} RpcValue(int i) : value(static_cast(i)) {} RpcValue(int64_t i) : value(i) {} RpcValue(double d) : value(d) {} RpcValue(const char* s) : value(std::string(s)) {} RpcValue(std::string s) : value(std::move(s)) {} RpcValue(RpcBytes b) : value(std::move(b)) {} RpcValue(RpcList l) : value(std::move(l)) {} RpcValue(RpcMap m) : value(std::move(m)) {} bool isNull() const { return std::holds_alternative(value); } bool isBool() const { return std::holds_alternative(value); } bool isInt() const { return std::holds_alternative(value); } bool isDouble() const { return std::holds_alternative(value); } bool isString() const { return std::holds_alternative(value); } bool isBytes() const { return std::holds_alternative(value); } bool isList() const { return std::holds_alternative(value); } bool isMap() const { return std::holds_alternative(value); } bool asBool() const { return std::get(value); } int64_t asInt() const { return std::get(value); } double asDouble() const { return std::get(value); } const std::string& asString() const { return std::get(value); } const RpcBytes& asBytes() const { return std::get(value); } const RpcList& asList() const { return std::get(value); } const RpcMap& asMap() const { return std::get(value); } bool operator==(const RpcValue& other) const { return value == other.value; } bool operator!=(const RpcValue& other) const { return !(*this == other); } }; // ── RpcMap out-of-line methods (need complete RpcValue) ──────────────────── inline void RpcMap::emplace(std::string key, RpcValue val) { entries.emplace_back(std::move(key), std::move(val)); } inline const RpcValue* RpcMap::find(const std::string& key) const { for (const auto& kv : entries) if (kv.first == key) return &kv.second; return nullptr; } inline const RpcValue& RpcMap::at(const std::string& key) const { const RpcValue* v = find(key); if (!v) throw std::out_of_range("RpcMap::at: key not found: " + key); return *v; } inline bool RpcMap::operator==(const RpcMap& other) const { return entries == other.entries; } } // namespace logos::plain #endif // LOGOS_PLAIN_RPC_VALUE_H