Skip to content

Commit

Permalink
Began implementing the tagged union without field names
Browse files Browse the repository at this point in the history
  • Loading branch information
Patrick Urbanke committed Jun 17, 2024
1 parent 1bec4b3 commit 94d49da
Show file tree
Hide file tree
Showing 12 changed files with 86 additions and 24 deletions.
2 changes: 1 addition & 1 deletion include/rfl/bson/Reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ struct Reader {
T::from_bson_obj(var);
});

rfl::Result<InputVarType> get_field(
rfl::Result<InputVarType> get_field_from_object(
const std::string& _name, const InputObjectType& _obj) const noexcept {
bson_t b;
bson_iter_t iter;
Expand Down
2 changes: 1 addition & 1 deletion include/rfl/cbor/Reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ struct Reader {
T::from_cbor_obj(var);
});

rfl::Result<InputVarType> get_field(
rfl::Result<InputVarType> get_field_from_object(
const std::string& _name, const InputObjectType& _obj) const noexcept {
InputVarType var;
auto buffer = std::vector<char>();
Expand Down
2 changes: 1 addition & 1 deletion include/rfl/flexbuf/Reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ struct Reader {
template <class T>
static constexpr bool has_custom_constructor = has_from_flexbuf<T>::value;

rfl::Result<InputVarType> get_field(
rfl::Result<InputVarType> get_field_from_object(
const std::string& _name, const InputObjectType& _obj) const noexcept {
const auto keys = _obj.Keys();
for (size_t i = 0; i < keys.size(); ++i) {
Expand Down
11 changes: 10 additions & 1 deletion include/rfl/json/Reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,16 @@ struct Reader {
T::from_json_obj(var);
});

rfl::Result<InputVarType> get_field(
rfl::Result<InputVarType> get_field_from_array(
const size_t _idx, const InputArrayType _arr) const noexcept {
const auto var = InputVarType(yyjson_arr_get(_arr.val_, _idx));
if (!var.val_) {
return rfl::Error("Index " + std::to_string(_idx) + " of of bounds.");
}
return var;
}

rfl::Result<InputVarType> get_field_from_object(
const std::string& _name, const InputObjectType _obj) const noexcept {
const auto var = InputVarType(yyjson_obj_get(_obj.val_, _name.c_str()));
if (!var.val_) {
Expand Down
2 changes: 1 addition & 1 deletion include/rfl/msgpack/Reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ struct Reader {
T::from_msgpack_obj(var);
});

rfl::Result<InputVarType> get_field(
rfl::Result<InputVarType> get_field_from_object(
const std::string& _name, const InputObjectType& _obj) const noexcept {
for (uint32_t i = 0; i < _obj.size; ++i) {
const auto& key = _obj.ptr[i].key;
Expand Down
9 changes: 7 additions & 2 deletions include/rfl/parsing/IsReader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ concept IsReader = requires(R r, std::string name,
MockObjectReader<R> object_reader,
typename R::InputArrayType arr,
typename R::InputObjectType obj,
typename R::InputVarType var) {
typename R::InputVarType var, size_t idx) {
/// Any Reader needs to define the following:
///
/// 1) An InputArrayType, which must be an array-like data structure.
Expand All @@ -48,9 +48,14 @@ concept IsReader = requires(R r, std::string name,
/// whether the class in question as a custom constructor, which might
/// be called something like from_json_obj(...).

/// Retrieves a particular field from an array.
{
r.get_field_from_array(idx, arr)
} -> std::same_as<rfl::Result<typename R::InputVarType>>;

/// Retrieves a particular field from an object.
{
r.get_field(name, obj)
r.get_field_from_object(name, obj)
} -> std::same_as<rfl::Result<typename R::InputVarType>>;

/// Determines whether a variable is empty (the NULL type).
Expand Down
41 changes: 29 additions & 12 deletions include/rfl/parsing/Parser_tagged_union.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,16 @@ struct Parser<R, W, TaggedUnion<_discriminator, AlternativeTypes...>,
using OutputObjectType = typename W::OutputObjectType;
using OutputVarType = typename W::OutputVarType;

constexpr static bool no_field_names_ = ProcessorsType::no_field_names_;

using InputObjectOrArrayType =
std::conditional_t<no_field_names_, typename R::InputArrayType,
typename R::InputObjectType>;

static ResultType read(const R& _r, const InputVarType& _var) noexcept {
const auto get_disc = [&_r](InputObjectType _obj) -> Result<std::string> {
return get_discriminator(_r, _obj);
const auto get_disc =
[&_r](InputObjectOrArrayType _obj_or_arr) -> Result<std::string> {
return get_discriminator(_r, _obj_or_arr);
};

const auto to_result =
Expand All @@ -42,7 +49,11 @@ struct Parser<R, W, TaggedUnion<_discriminator, AlternativeTypes...>,
std::make_integer_sequence<int, sizeof...(AlternativeTypes)>());
};

return _r.to_object(_var).and_then(get_disc).and_then(to_result);
if constexpr (no_field_names_) {
return _r.to_array(_var).and_then(get_disc).and_then(to_result);
} else {
return _r.to_object(_var).and_then(get_disc).and_then(to_result);
}
}

template <class P>
Expand Down Expand Up @@ -115,22 +126,28 @@ struct Parser<R, W, TaggedUnion<_discriminator, AlternativeTypes...>,
}
}

/// Retrieves the discriminator.
/// Retrieves the discriminator from an object
static Result<std::string> get_discriminator(
const R& _r, const InputObjectType& _obj) noexcept {
const R& _r, const InputObjectOrArrayType& _obj_or_arr) noexcept {
const auto to_type = [&_r](auto _var) {
return _r.template to_basic_type<std::string>(_var);
};

const auto embellish_error = [](const auto& _err) {
return Error("Could not parse tagged union: Could not find field '" +
_discriminator.str() +
"' or type of field was not a string.");
};

const auto to_type = [&_r](auto _var) {
return _r.template to_basic_type<std::string>(_var);
};

return _r.get_field(_discriminator.str(), _obj)
.and_then(to_type)
.or_else(embellish_error);
if constexpr (no_field_names_) {
return _r.get_field_from_array(0, _obj_or_arr)
.and_then(to_type)
.or_else(embellish_error);
} else {
return _r.get_field_from_object(_discriminator.str(), _obj_or_arr)
.and_then(to_type)
.or_else(embellish_error);
}
}

/// Determines whether the discriminating literal contains the value
Expand Down
2 changes: 1 addition & 1 deletion include/rfl/toml/Reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ struct Reader {
T::from_toml_obj(var);
});

rfl::Result<InputVarType> get_field(
rfl::Result<InputVarType> get_field_from_object(
const std::string& _name, const InputObjectType& _obj) const noexcept {
auto var = (*_obj)[_name];
if (!var) {
Expand Down
2 changes: 1 addition & 1 deletion include/rfl/xml/Reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ struct Reader {
return std::visit(cast, _node_or_attribute);
}

rfl::Result<InputVarType> get_field(
rfl::Result<InputVarType> get_field_from_object(
const std::string& _name, const InputObjectType _obj) const noexcept {
const auto node = _obj.node_.child(_name.c_str());
if (!node) {
Expand Down
2 changes: 1 addition & 1 deletion include/rfl/yaml/Reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ struct Reader {
T::from_yaml_obj(var);
});

rfl::Result<InputVarType> get_field(
rfl::Result<InputVarType> get_field_from_object(
const std::string& _name, const InputObjectType& _obj) const noexcept {
auto var = InputVarType(_obj.node_[_name]);
if (!var.node_) {
Expand Down
2 changes: 0 additions & 2 deletions tests/json/test_json_schema3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ TEST(json, test_json_schema3) {
const auto json_schema =
rfl::json::to_schema<DescribedType, rfl::NoFieldNames>();

std::cout << json_schema << std::endl << std::endl;

const std::string expected =
R"({"$schema":"https://json-schema.org/draft/2020-12/schema","$ref":"#/definitions/test_json_schema3__Person","description":"JSON schema that describes the required attributes for the person class.","definitions":{"test_json_schema3__Circle":{"type":"array","prefixItems":[{"type":"number"}],"items":false},"test_json_schema3__Circle__tagged":{"type":"array","prefixItems":[{"type":"string","enum":["Circle"]},{"type":"number"}],"items":false},"test_json_schema3__Person":{"type":"array","prefixItems":[{"type":"string"},{"type":"string"},{"type":"string","description":"Must be a proper email in the form xxx@xxx.xxx.","pattern":"^[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}$"},{"type":"string"},{"type":"string","enum":["red","green","blue"]},{"allOf":[{"minimum":0,"type":"integer"},{"maximum":130,"type":"integer"}]},{"type":"number"},{"type":"array","description":"The person's children. Pass an empty array for no children.","items":{"$ref":"#/definitions/test_json_schema3__Person"}},{"anyOf":[{"type":"string","enum":["red","green","blue"]},{"type":"array","items":{"$ref":"#/definitions/test_json_schema3__Person"}},{"type":"integer"}]},{"type":"array","prefixItems":[{"type":"string","enum":["red","green","blue"]},{"type":"array","items":{"$ref":"#/definitions/test_json_schema3__Person"}},{"type":"integer"}],"items":false},{"anyOf":[{"$ref":"#/definitions/test_json_schema3__Rectangle__tagged"},{"$ref":"#/definitions/test_json_schema3__Square__tagged"},{"$ref":"#/definitions/test_json_schema3__Circle__tagged"}]},{"anyOf":[{"type":"array","prefixItems":[{"$ref":"#/definitions/test_json_schema3__Rectangle"}],"items":false},{"type":"array","prefixItems":[{"$ref":"#/definitions/test_json_schema3__Square"}],"items":false},{"type":"array","prefixItems":[{"$ref":"#/definitions/test_json_schema3__Circle"}],"items":false}]}],"items":false},"test_json_schema3__Rectangle":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"}],"items":false},"test_json_schema3__Rectangle__tagged":{"type":"array","prefixItems":[{"type":"string","enum":["Rectangle"]},{"type":"number"},{"type":"number"}],"items":false},"test_json_schema3__Square":{"type":"array","prefixItems":[{"type":"number"}],"items":false},"test_json_schema3__Square__tagged":{"type":"array","prefixItems":[{"type":"string","enum":["Square"]},{"type":"number"}],"items":false}}})";

Expand Down
33 changes: 33 additions & 0 deletions tests/json/test_tagged_union4.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <cassert>
#include <iostream>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <source_location>
#include <string>
#include <vector>

#include "write_and_read.hpp"

namespace test_tagged_union4 {

struct Circle {
double radius;
};

struct Rectangle {
double height;
double width;
};

struct Square {
double width;
};

using Shapes = rfl::TaggedUnion<"shape", Circle, Square, Rectangle>;

TEST(json, test_tagged_union4) {
const Shapes r = Rectangle{.height = 10, .width = 5};

write_and_read<rfl::NoFieldNames>(r, R"(["Rectangle",10.0,5.0])");
}
} // namespace test_tagged_union4

0 comments on commit 94d49da

Please sign in to comment.