#ifndef RFL_CBOR_READER_HPP_ #define RFL_CBOR_READER_HPP_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../Box.hpp" #include "../Result.hpp" #include "../always_false.hpp" namespace rfl { namespace cbor { /// Please refer to https://intel.github.io/tinycbor/current/index.html struct Reader { struct CBORInputArray { CborValue* val_; }; struct CBORInputObject { CborValue* val_; }; struct CBORInputVar { CborValue* val_; }; using InputArrayType = CBORInputArray; using InputObjectType = CBORInputObject; using InputVarType = CBORInputVar; template static constexpr bool has_custom_constructor = (requires(InputVarType var) { T::from_cbor_obj(var); }); rfl::Result get_field(const std::string& _name, const InputObjectType& _obj) const noexcept { CborValue val; auto buffer = std::vector(); auto err = cbor_value_enter_container(_obj.val_, &val); if (err != CborNoError) { return Error(cbor_error_string(err)); } size_t length = 0; err = cbor_value_get_map_length(_obj.val_, &length); if (err != CborNoError) { return Error(cbor_error_string(err)); } for (size_t i = 0; i < length; ++i) { if (!cbor_value_is_text_string(&val)) { return Error("Expected the key to be a string value."); } err = get_string(&val, &buffer); if (err != CborNoError) { return Error(cbor_error_string(err)); } err = cbor_value_advance(&val); if (err != CborNoError) { return Error(cbor_error_string(err)); } if (_name == buffer.data()) { return to_input_var(&val); } err = cbor_value_advance(&val); if (err != CborNoError) { return Error(cbor_error_string(err)); } } return Error("No field named '" + _name + "' was found."); } bool is_empty(const InputVarType& _var) const noexcept { return cbor_value_is_null(_var.val_); } template rfl::Result to_basic_type(const InputVarType& _var) const noexcept { if constexpr (std::is_same, std::string>()) { if (!cbor_value_is_text_string(_var.val_)) { return Error("Could not cast to string."); } std::vector buffer; const auto err = get_string(_var.val_, &buffer); if (err != CborNoError) { return Error(cbor_error_string(err)); } return std::string(buffer.data()); } else if constexpr (std::is_same, bool>()) { if (!cbor_value_is_boolean(_var.val_)) { return rfl::Error("Could not cast to boolean."); } bool result = false; const auto err = cbor_value_get_boolean(_var.val_, &result); if (err != CborNoError) { return Error(cbor_error_string(err)); } return result; } else if constexpr (std::is_floating_point>() || std::is_integral>()) { if (cbor_value_is_integer(_var.val_)) { std::int64_t result = 0; const auto err = cbor_value_get_int64(_var.val_, &result); if (err != CborNoError) { return Error(cbor_error_string(err)); } return static_cast(result); } else if (cbor_value_is_float(_var.val_)) { float result = 0.0; const auto err = cbor_value_get_float(_var.val_, &result); if (err != CborNoError) { return Error(cbor_error_string(err)); } return static_cast(result); } else if (cbor_value_is_double(_var.val_)) { double result = 0.0; const auto err = cbor_value_get_double(_var.val_, &result); if (err != CborNoError) { return Error(cbor_error_string(err)); } return static_cast(result); } return rfl::Error( "Could not cast to numeric value. The type must be integral, " "float " "or double." ); } else { static_assert(rfl::always_false_v, "Unsupported type."); } } rfl::Result to_array(const InputVarType& _var) const noexcept { if (!cbor_value_is_array(_var.val_)) { return Error("Could not cast to an array."); } return InputArrayType { _var.val_ }; } rfl::Result to_object(const InputVarType& _var) const noexcept { if (!cbor_value_is_map(_var.val_)) { return Error("Could not cast to an object."); } return InputObjectType { _var.val_ }; } template std::optional read_array(const ArrayReader& _array_reader, const InputArrayType& _arr) const noexcept { CborValue val; auto buffer = std::vector(); auto err = cbor_value_enter_container(_arr.val_, &val); if (err != CborNoError && err != CborErrorOutOfMemory) { return Error(cbor_error_string(err)); } size_t length = 0; err = cbor_value_get_array_length(_arr.val_, &length); if (err != CborNoError && err != CborErrorOutOfMemory) { return Error(cbor_error_string(err)); } for (size_t i = 0; i < length; ++i) { const auto err2 = _array_reader.read(to_input_var(&val)); if (err2) { return err2; } err = cbor_value_advance(&val); if (err != CborNoError && err != CborErrorOutOfMemory) { return Error(cbor_error_string(err)); } } return std::nullopt; } template std::optional read_object(const ObjectReader& _object_reader, const InputObjectType& _obj) const noexcept { size_t length = 0; auto err = cbor_value_get_map_length(_obj.val_, &length); if (err != CborNoError) { return Error(cbor_error_string(err)); } CborValue val; err = cbor_value_enter_container(_obj.val_, &val); if (err != CborNoError) { return Error(cbor_error_string(err)); } auto buffer = std::vector(); for (size_t i = 0; i < length; ++i) { err = get_string(&val, &buffer); if (err != CborNoError) { return Error(cbor_error_string(err)); } err = cbor_value_advance(&val); if (err != CborNoError) { return Error(cbor_error_string(err)); } const auto name = std::string_view(buffer.data(), buffer.size() - 1); _object_reader.read(name, InputVarType { &val }); cbor_value_advance(&val); } return std::nullopt; } template rfl::Result use_custom_constructor(const InputVarType& _var) const noexcept { try { return T::from_cbor_obj(_var); } catch (std::exception& e) { return rfl::Error(e.what()); } } private: CborError get_string(const CborValue* _ptr, std::vector* _buffer) const noexcept { size_t length = 0; auto err = cbor_value_get_string_length(_ptr, &length); if (err != CborNoError && err != CborErrorOutOfMemory) { return err; } _buffer->resize(length + 1); (*_buffer)[length] = '\0'; return cbor_value_copy_text_string(_ptr, _buffer->data(), &length, NULL); } InputVarType to_input_var(CborValue* _ptr) const noexcept { values_->emplace_back(rfl::Box::make(*_ptr)); auto* last_value = values_->back().get(); return InputVarType { last_value }; } private: /// Contains the values inside the object. rfl::Box>> values_; }; } // namespace cbor } // namespace rfl #endif // JSON_PARSER_HPP_