Skip to content

Commit f0af706

Browse files
Allows usage of custom Reflector for enums types (#621)
1 parent f2d3bc8 commit f0af706

3 files changed

Lines changed: 158 additions & 3 deletions

File tree

include/rfl/parsing/Parser_enum.hpp

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "../Result.hpp"
88
#include "../enums.hpp"
99
#include "../thirdparty/enchantum/enchantum.hpp"
10+
#include "../internal/has_reflector.hpp"
1011
#include "AreReaderAndWriter.hpp"
1112
#include "Parent.hpp"
1213
#include "Parser_base.hpp"
@@ -29,7 +30,20 @@ struct Parser<R, W, T, ProcessorsType> {
2930

3031
/// Expresses the variables as type T.
3132
static Result<T> read(const R& _r, const InputVarType& _var) noexcept {
32-
if constexpr (ProcessorsType::underlying_enums_ ||
33+
if constexpr (internal::has_read_reflector<T>) {
34+
const auto wrap_in_t = [](auto&& _named_tuple) -> Result<T> {
35+
try {
36+
using NT = decltype(_named_tuple);
37+
return Reflector<T>::to(std::forward<NT>(_named_tuple));
38+
} catch (std::exception& e) {
39+
return error(e.what());
40+
}
41+
};
42+
return Parser<R, W, typename Reflector<T>::ReflType,
43+
ProcessorsType>::read(_r, _var)
44+
.and_then(wrap_in_t);
45+
46+
} else if constexpr (ProcessorsType::underlying_enums_ ||
3347
schemaful::IsSchemafulReader<R>) {
3448
static_assert(enchantum::ScopedEnum<T>,
3549
"The enum must be a scoped enum in order to retrieve "
@@ -44,7 +58,10 @@ struct Parser<R, W, T, ProcessorsType> {
4458

4559
template <class P>
4660
static void write(const W& _w, const T& _var, const P& _parent) {
47-
if constexpr (ProcessorsType::underlying_enums_ ||
61+
if constexpr (internal::has_write_reflector<T>) {
62+
Parser<R, W, typename Reflector<T>::ReflType, ProcessorsType>::write(
63+
_w, Reflector<T>::from(_var), _parent);
64+
} else if constexpr (ProcessorsType::underlying_enums_ ||
4865
schemaful::IsSchemafulWriter<W>) {
4966
const auto val = static_cast<std::underlying_type_t<T>>(_var);
5067
ParentType::add_value(_w, val, _parent);
@@ -57,7 +74,12 @@ struct Parser<R, W, T, ProcessorsType> {
5774
static schema::Type to_schema(
5875
std::map<std::string, schema::Type>* _definitions) {
5976
using U = std::remove_cvref_t<T>;
60-
return make_enum<U>(_definitions);
77+
if constexpr (internal::has_read_reflector<U> ||
78+
internal::has_write_reflector<U>) {
79+
return make_reference<U>(_definitions);
80+
} else {
81+
return make_enum<U>(_definitions);
82+
}
6183
}
6284

6385
private:
@@ -77,6 +99,19 @@ struct Parser<R, W, T, ProcessorsType> {
7799
ProcessorsType>::to_schema(_definitions);
78100
}
79101
}
102+
103+
template <class U>
104+
static schema::Type make_reference(
105+
std::map<std::string, schema::Type>* _definitions) {
106+
using Type = schema::Type;
107+
const auto name = make_type_name<U>();
108+
109+
if (_definitions->find(name) == _definitions->end()) {
110+
(*_definitions)[name] = Parser<R, W, typename Reflector<U>::ReflType,
111+
ProcessorsType>::to_schema(_definitions);
112+
}
113+
return Type{Type::Reference{name}};
114+
}
80115
};
81116

82117
} // namespace rfl::parsing

tests/json/test_custom_enum1.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include <rfl.hpp>
2+
#include <rfl/json.hpp>
3+
#include <string>
4+
5+
#include "write_and_read.hpp"
6+
7+
namespace test_custom_enum1 {
8+
9+
enum class Role {
10+
Normal = 0,
11+
Admin = 1,
12+
SuperAdmin = 0xFF,
13+
};
14+
15+
struct User {
16+
std::string id;
17+
Role role;
18+
std::string username;
19+
};
20+
21+
} // namespace test_custom_enum1
22+
23+
24+
namespace rfl {
25+
template <>
26+
struct Reflector<test_custom_enum1::Role> {
27+
using ReflType = uint32_t;
28+
29+
static test_custom_enum1::Role to(const ReflType& v) {
30+
test_custom_enum1::Role rv = static_cast<test_custom_enum1::Role>(v);
31+
switch (rv) {
32+
case test_custom_enum1::Role::Normal:
33+
case test_custom_enum1::Role::Admin:
34+
case test_custom_enum1::Role::SuperAdmin:
35+
return rv;
36+
default:
37+
throw "invalid role value";
38+
}
39+
}
40+
41+
static ReflType from(const test_custom_enum1::Role& v) {
42+
return static_cast<ReflType>(v);
43+
}
44+
};
45+
};
46+
47+
namespace test_custom_enum1 {
48+
49+
TEST(json, test_custom_enum1) {
50+
const auto test1 = test_custom_enum1::User{
51+
.id = "123",
52+
.role = test_custom_enum1::Role::SuperAdmin,
53+
.username = "bart",
54+
};
55+
const auto test2 = rfl::json::read<test_custom_enum1::User>(
56+
rfl::json::write(test1)).value();
57+
58+
write_and_read(test2, R"({"id":"123","role":255,"username":"bart"})");
59+
}
60+
} // namespace test_custom_class4

tests/json/test_json_schema6.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include <rfl.hpp>
2+
#include <rfl/json.hpp>
3+
#include <string>
4+
5+
#include "write_and_read.hpp"
6+
7+
8+
namespace test_json_schema6 {
9+
10+
enum class Role {
11+
Normal = 0,
12+
Admin = 1,
13+
SuperAdmin = 0xFF,
14+
};
15+
16+
struct User {
17+
std::string id;
18+
Role role;
19+
std::string username;
20+
};
21+
22+
} // namespace test_json_schema6
23+
24+
25+
namespace rfl {
26+
template <>
27+
struct Reflector<test_json_schema6::Role> {
28+
using ReflType = uint32_t;
29+
30+
static test_json_schema6::Role to(const ReflType& v) {
31+
test_json_schema6::Role rv = static_cast<test_json_schema6::Role>(v);
32+
switch (rv) {
33+
case test_json_schema6::Role::Normal:
34+
case test_json_schema6::Role::Admin:
35+
case test_json_schema6::Role::SuperAdmin:
36+
return rv;
37+
default:
38+
throw "invalid role value";
39+
}
40+
}
41+
42+
static ReflType from(const test_json_schema6::Role& v) {
43+
return static_cast<ReflType>(v);
44+
}
45+
};
46+
};
47+
48+
49+
50+
namespace test_json_schema6 {
51+
52+
TEST(json, test_json_schema6) {
53+
const auto json_schema = rfl::json::to_schema<test_json_schema6::User>();
54+
55+
const std::string expected =
56+
R"({"$schema":"https://json-schema.org/draft/2020-12/schema","$ref":"#/$defs/test_json_schema6__User","$defs":{"test_json_schema6__Role":{"type":"integer"},"test_json_schema6__User":{"type":"object","properties":{"id":{"type":"string"},"role":{"$ref":"#/$defs/test_json_schema6__Role"},"username":{"type":"string"}},"required":["id","role","username"]}}})";
57+
58+
EXPECT_EQ(json_schema, expected);
59+
}
60+
} // namespace test_json_schema6

0 commit comments

Comments
 (0)