#ifndef RFL_BSON_WRITER_HPP_ #define RFL_BSON_WRITER_HPP_ #include #include #include #include #include #include #include #include #include #include #include "../Box.hpp" #include "../Ref.hpp" #include "../Result.hpp" #include "../always_false.hpp" namespace rfl { namespace bson { /// Please refer to https://mongoc.org/libbson/current/api.html class Writer { struct BSONType { bson_t val_; }; struct IsArray { bson_array_builder_t* ptr_; }; struct IsObject { bson_t* ptr_; }; struct IsRoot {}; using ParentType = std::variant; public: struct BSONOutputArray { BSONOutputArray(bson_array_builder_t* _val, ParentType _parent) : parent_(_parent), val_(_val) {} ParentType parent_; bson_array_builder_t* val_; }; struct BSONOutputObject { BSONOutputObject(bson_t* _val, ParentType _parent) : parent_(_parent), val_(_val) {} ParentType parent_; bson_t* val_; }; struct BSONOutputVar {}; using OutputArrayType = BSONOutputArray; using OutputObjectType = BSONOutputObject; using OutputVarType = BSONOutputVar; Writer(bson_t* _doc) : doc_(_doc) {} ~Writer() = default; OutputArrayType array_as_root(const size_t _size) const noexcept { bson_array_builder_t* val = bson_array_builder_new(); return OutputArrayType(val, IsRoot{}); } OutputObjectType object_as_root(const size_t _size) const noexcept { return OutputObjectType(doc_, IsRoot{}); } OutputVarType null_as_root() const noexcept { // Appears to be unsupported by the BSON C API. return OutputVarType{}; } template OutputVarType value_as_root(const T& _var) const noexcept { static_assert(rfl::always_false_v, "BSON only allows arrays or objects as its root."); return OutputVarType{}; } OutputArrayType add_array_to_array(const size_t _size, OutputArrayType* _parent) const noexcept { bson_array_builder_t* val; bson_array_builder_append_array_builder_begin(_parent->val_, &val); return OutputArrayType(val, IsArray{_parent->val_}); } OutputArrayType add_array_to_object( const std::string_view& _name, const size_t _size, OutputObjectType* _parent) const noexcept { bson_array_builder_t* val; bson_append_array_builder_begin(_parent->val_, _name.data(), static_cast(_name.size()), &val); return OutputArrayType(val, IsObject{_parent->val_}); } OutputObjectType add_object_to_array( const size_t _size, OutputArrayType* _parent) const noexcept { subdocs_->emplace_back(rfl::Box()); bson_array_builder_append_document_begin(_parent->val_, &(subdocs_->back()->val_)); return OutputObjectType(&subdocs_->back()->val_, IsArray{_parent->val_}); } OutputObjectType add_object_to_object( const std::string_view& _name, const size_t _size, OutputObjectType* _parent) const noexcept { subdocs_->emplace_back(rfl::Box()); bson_append_document_begin(_parent->val_, _name.data(), static_cast(_name.size()), &(subdocs_->back()->val_)); return OutputObjectType(&subdocs_->back()->val_, IsObject{_parent->val_}); } template OutputVarType add_value_to_array(const T& _var, OutputArrayType* _parent) const noexcept { if constexpr (std::is_same, std::string>()) { bson_array_builder_append_utf8(_parent->val_, _var.c_str(), static_cast(_var.size())); } else if constexpr (std::is_same, bool>()) { bson_array_builder_append_bool(_parent->val_, _var); } else if constexpr (std::is_floating_point>()) { bson_array_builder_append_double(_parent->val_, static_cast(_var)); } else if constexpr (std::is_integral>()) { bson_array_builder_append_int64(_parent->val_, static_cast(_var)); } else if constexpr (std::is_same, bson_oid_t>()) { bson_array_builder_append_oid(_parent->val_, &_var); } else { static_assert(rfl::always_false_v, "Unsupported type."); } return OutputVarType{}; } template OutputVarType add_value_to_object(const std::string_view& _name, const T& _var, OutputObjectType* _parent) const noexcept { if constexpr (std::is_same, std::string>()) { bson_append_utf8(_parent->val_, _name.data(), static_cast(_name.size()), _var.c_str(), static_cast(_var.size())); } else if constexpr (std::is_same, bool>()) { bson_append_bool(_parent->val_, _name.data(), static_cast(_name.size()), _var); } else if constexpr (std::is_floating_point>()) { bson_append_double(_parent->val_, _name.data(), static_cast(_name.size()), static_cast(_var)); } else if constexpr (std::is_integral>()) { bson_append_int64(_parent->val_, _name.data(), static_cast(_name.size()), static_cast(_var)); } else if constexpr (std::is_same, bson_oid_t>()) { bson_append_oid(_parent->val_, _name.data(), static_cast(_name.size()), &_var); } else { static_assert(rfl::always_false_v, "Unsupported type."); } return OutputVarType{}; } OutputVarType add_null_to_array(OutputArrayType* _parent) const noexcept { bson_array_builder_append_null(_parent->val_); return OutputVarType{}; } OutputVarType add_null_to_object(const std::string_view& _name, OutputObjectType* _parent) const noexcept { bson_append_null(_parent->val_, _name.data(), static_cast(_name.size())); return OutputVarType{}; } void end_array(OutputArrayType* _arr) const noexcept { const auto handle = [&](const auto _parent) { using Type = std::remove_cvref_t; if constexpr (std::is_same()) { bson_array_builder_append_array_builder_end(_parent.ptr_, _arr->val_); } else if constexpr (std::is_same()) { bson_append_array_builder_end(_parent.ptr_, _arr->val_); } else if constexpr (std::is_same()) { bson_array_builder_build(_arr->val_, doc_); } else { static_assert(rfl::always_false_v, "Unsupported type."); } }; std::visit(handle, _arr->parent_); } void end_object(OutputObjectType* _obj) const noexcept { const auto handle = [&](const auto _parent) { using Type = std::remove_cvref_t; if constexpr (std::is_same()) { bson_array_builder_append_document_end(_parent.ptr_, _obj->val_); } else if constexpr (std::is_same()) { bson_append_document_end(_parent.ptr_, _obj->val_); } else if constexpr (std::is_same()) { } else { static_assert(rfl::always_false_v, "Unsupported type."); } }; std::visit(handle, _obj->parent_); } private: /// Pointer to the main document. In BSON, documents are what are usually /// called objects. bson_t* const doc_; /// Contain all of the subdocuments. const rfl::Ref>> subdocs_; }; } // namespace bson } // namespace rfl #endif // BSON_PARSER_HPP_