#pragma once // v1.11 Phase B — binary in-memory document storage. // // Wraps a yyjson_mut_doc as the canonical Document::data representation, // exposing encode/decode/get_field/to_json_text. The yyjson types are // forward-declared here so this header does NOT transitively pull in // — only doc_binary.cpp links against yyjson. Callsites that // only need the public surface stay yyjson-agnostic. // // See docs/superpowers/specs/2026-05-15-binary-doc-format-decision.md for // the format-choice rationale and lifecycle contract. #include #include #include #include // Forward declarations — keep out of the public header. struct yyjson_mut_doc; struct yyjson_mut_val; namespace smartbotic::db::doc_binary { // Owning, move-only RAII wrapper around a yyjson_mut_doc*. The destructor // calls yyjson_mut_doc_free, so any Doc constructed from a raw pointer takes // ownership of that pointer. class Doc { public: Doc() = default; explicit Doc(yyjson_mut_doc* doc); Doc(Doc&& other) noexcept; Doc& operator=(Doc&& other) noexcept; // Deep-copy via yyjson_mut_val_mut_copy. Phase B T3 deviation: the // decision doc originally listed Doc as move-only, but the surrounding // codebase copies Document by value in too many places (vector // returns, query results, replication paths) to refactor safely. Copies // are O(size) and allocate a fresh yyjson_mut_doc. Doc(const Doc& other); Doc& operator=(const Doc& other); ~Doc(); // True if this Doc holds no underlying yyjson document. bool empty() const noexcept; // Root value of the underlying doc, or nullptr if empty. yyjson_mut_val* root() const noexcept; // Raw underlying pointer (or nullptr). Caller must not free. yyjson_mut_doc* raw() const noexcept; private: yyjson_mut_doc* doc_ = nullptr; }; // Encode an nlohmann::json tree into a freshly-allocated yyjson mut_doc. // Strings are COPIED into yyjson (yyjson_mut_strncpy) so the source can be // destroyed safely. Throws std::bad_alloc on yyjson allocator failure. Doc encode(const nlohmann::json& j); // Decode a Doc back to nlohmann::json (full-tree materialise). Used by the // lazy view and any caller that needs full-tree semantics. An empty Doc // returns nlohmann::json(nullptr). nlohmann::json decode(const Doc& doc); // Field-fast-path. Returns nullopt if the field is absent or if the root is // not an object. Walks only the immediate children of the root (NOT // recursive). For nested access, callers should decode() and walk the // resulting nlohmann tree. std::optional get_field(const Doc& doc, std::string_view field_name); // Serialise the Doc back to compact JSON text. Used at WAL/snapshot write // sites and gRPC egress. An empty Doc returns "null". std::string to_json_text(const Doc& doc); } // namespace smartbotic::db::doc_binary