Skip to content

Commit b805fe3

Browse files
Fix infinite recursion; fixes #677 (#678)
1 parent e4de84d commit b805fe3

6 files changed

Lines changed: 75 additions & 88 deletions

File tree

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
11
#ifndef RFL_INTERNAL_ALLFIELDS_HPP_
22
#define RFL_INTERNAL_ALLFIELDS_HPP_
33

4-
#include "../Tuple.hpp"
54
#include "is_field.hpp"
65

7-
namespace rfl {
8-
namespace internal {
6+
namespace rfl::internal {
97

10-
template <class TupleType, int _i = 0>
11-
constexpr bool all_fields() {
12-
if constexpr (_i == rfl::tuple_size_v<TupleType>) {
13-
return true;
14-
} else {
15-
using T = tuple_element_t<_i, TupleType>;
16-
return is_field_v<T> && all_fields<TupleType, _i + 1>();
17-
}
18-
}
8+
template <class... Ts>
9+
constexpr bool all_fields_v = (is_field_v<Ts> && ...);
1910

20-
} // namespace internal
21-
} // namespace rfl
11+
} // namespace rfl::internal
2212

2313
#endif

include/rfl/internal/is_field.hpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
#include "../Field.hpp"
77
#include "StringLiteral.hpp"
88

9-
namespace rfl {
10-
namespace internal {
9+
namespace rfl::internal {
1110

1211
template <class T>
1312
class is_field;
@@ -22,7 +21,6 @@ template <class T>
2221
constexpr bool is_field_v =
2322
is_field<std::remove_cvref_t<std::remove_pointer_t<T>>>::value;
2423

25-
} // namespace internal
26-
} // namespace rfl
24+
} // namespace rfl::internal
2725

2826
#endif

include/rfl/parsing/FieldVariantParser.hpp

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,18 @@
55
#include <string>
66
#include <type_traits>
77

8+
#include "../NamedTuple.hpp"
89
#include "../Result.hpp"
910
#include "../Tuple.hpp"
1011
#include "../Variant.hpp"
11-
#include "../always_false.hpp"
12-
#include "../visit.hpp"
12+
#include "../internal/no_duplicate_field_names.hpp"
13+
#include "../internal/to_ptr_field.hpp"
14+
#include "../make_named_tuple.hpp"
1315
#include "FieldVariantReader.hpp"
1416
#include "Parser_base.hpp"
1517
#include "schema/Type.hpp"
1618

17-
namespace rfl {
18-
namespace parsing {
19+
namespace rfl::parsing {
1920

2021
/// To be used when all options of the variants are rfl::Field. Essentially,
2122
/// this is an externally tagged union.
@@ -93,13 +94,21 @@ struct FieldVariantParser {
9394
* @return The schema type.
9495
*/
9596
static schema::Type to_schema(
96-
std::map<std::string, schema::Type>* _definitions,
97-
std::vector<schema::Type> = {}) {
98-
using VariantType = rfl::Variant<NamedTuple<FieldTypes>...>;
99-
return Parser<R, W, VariantType, ProcessorsType>::to_schema(_definitions);
97+
std::map<std::string, schema::Type>* _definitions) {
98+
return schema::Type{schema::Type::AnyOf{
99+
.types_ = std::vector<schema::Type>(
100+
{one_field_to_type<FieldTypes>(_definitions)...})}};
101+
}
102+
103+
private:
104+
template <class FieldType>
105+
static schema::Type one_field_to_type(
106+
std::map<std::string, schema::Type>* _definitions) noexcept {
107+
using NamedTupleType = NamedTuple<std::remove_cvref_t<FieldType>>;
108+
return Parser<R, W, NamedTupleType, ProcessorsType>::to_schema(
109+
_definitions);
100110
}
101111
};
102-
} // namespace parsing
103-
} // namespace rfl
112+
} // namespace rfl::parsing
104113

105114
#endif

include/rfl/parsing/ParserRflVariant.hpp

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66

77
#include "../Result.hpp"
88
#include "../Variant.hpp"
9-
#include "../always_false.hpp"
109
#include "../internal/add_tags_to_variants_v.hpp"
1110
#include "../internal/all_fields.hpp"
1211
#include "../internal/nth_element_t.hpp"
12+
#include "../visit.hpp"
1313
#include "FieldVariantParser.hpp"
1414
#include "Parent.hpp"
1515
#include "Parser_base.hpp"
@@ -52,7 +52,7 @@ class ParserRflVariant<R, W, rfl::Variant<AlternativeTypes...>,
5252
*/
5353
static Result<rfl::Variant<AlternativeTypes...>> read(
5454
const R& _r, const InputVarType& _var) noexcept {
55-
if constexpr (internal::all_fields<std::tuple<AlternativeTypes...>>()) {
55+
if constexpr (internal::all_fields_v<AlternativeTypes...>) {
5656
if constexpr (schemaful::IsSchemafulReader<R>) {
5757
using WrappedType = rfl::Variant<NamedTuple<AlternativeTypes>...>;
5858
return Parser<R, W, WrappedType, ProcessorsType>::read(_r, _var)
@@ -124,7 +124,7 @@ class ParserRflVariant<R, W, rfl::Variant<AlternativeTypes...>,
124124
static void write(const W& _w,
125125
const rfl::Variant<AlternativeTypes...>& _variant,
126126
const P& _parent) {
127-
if constexpr (internal::all_fields<std::tuple<AlternativeTypes...>>()) {
127+
if constexpr (internal::all_fields_v<AlternativeTypes...>) {
128128
if constexpr (schemaful::IsSchemafulWriter<W>) {
129129
using WrappedType = rfl::Variant<
130130
NamedTuple<Field<AlternativeTypes::name_,
@@ -186,7 +186,7 @@ class ParserRflVariant<R, W, rfl::Variant<AlternativeTypes...>,
186186
*/
187187
static schema::Type to_schema(
188188
std::map<std::string, schema::Type>* _definitions) {
189-
if constexpr (internal::all_fields<std::tuple<AlternativeTypes...>>()) {
189+
if constexpr (internal::all_fields_v<AlternativeTypes...>) {
190190
return FieldVariantParser<R, W, ProcessorsType,
191191
AlternativeTypes...>::to_schema(_definitions);
192192

@@ -201,29 +201,18 @@ class ParserRflVariant<R, W, rfl::Variant<AlternativeTypes...>,
201201
_definitions);
202202

203203
} else {
204-
std::vector<schema::Type> types;
205-
build_schema(
206-
_definitions, &types,
207-
std::make_integer_sequence<int, sizeof...(AlternativeTypes)>());
208-
return schema::Type{schema::Type::AnyOf{.types_ = std::move(types)}};
204+
return schema::Type{schema::Type::AnyOf{
205+
.types_ = std::vector<schema::Type>(
206+
{one_field_to_type<AlternativeTypes>(_definitions)...})}};
209207
}
210208
}
211209

212210
private:
213-
template <size_t _i>
214-
static void add_to_schema(std::map<std::string, schema::Type>* _definitions,
215-
std::vector<schema::Type>* _types) noexcept {
216-
using AltType =
217-
std::remove_cvref_t<internal::nth_element_t<_i, AlternativeTypes...>>;
218-
_types->push_back(
219-
Parser<R, W, AltType, ProcessorsType>::to_schema(_definitions));
220-
}
221-
222-
template <int... _is>
223-
static void build_schema(std::map<std::string, schema::Type>* _definitions,
224-
std::vector<schema::Type>* _types,
225-
std::integer_sequence<int, _is...>) noexcept {
226-
(add_to_schema<_is>(_definitions, _types), ...);
211+
template <class AltType>
212+
static schema::Type one_field_to_type(
213+
std::map<std::string, schema::Type>* _definitions) noexcept {
214+
return Parser<R, W, std::remove_cvref_t<AltType>,
215+
ProcessorsType>::to_schema(_definitions);
227216
}
228217

229218
template <int _i>

include/rfl/parsing/ParserVariant.hpp

Lines changed: 12 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@
77
#include <variant>
88

99
#include "../NamedTuple.hpp"
10-
#include "../Ref.hpp"
1110
#include "../Result.hpp"
1211
#include "../Variant.hpp"
13-
#include "../always_false.hpp"
1412
#include "../internal/add_tags_to_variants_v.hpp"
1513
#include "../internal/all_fields.hpp"
1614
#include "../internal/to_ptr_field.hpp"
15+
#include "../visit.hpp"
1716
#include "FieldVariantParser.hpp"
1817
#include "Parent.hpp"
1918
#include "Parser_base.hpp"
@@ -66,7 +65,7 @@ class ParserVariant<R, W, std::variant<AlternativeTypes...>, ProcessorsType> {
6665
*/
6766
static Result<std::variant<AlternativeTypes...>> read(
6867
const R& _r, const InputVarType& _var) noexcept {
69-
if constexpr (internal::all_fields<std::tuple<AlternativeTypes...>>()) {
68+
if constexpr (internal::all_fields_v<AlternativeTypes...>) {
7069
if constexpr (schemaful::IsSchemafulReader<R>) {
7170
using WrappedType = rfl::Variant<NamedTuple<AlternativeTypes>...>;
7271
return Parser<R, W, WrappedType, ProcessorsType>::read(_r, _var)
@@ -146,7 +145,7 @@ class ParserVariant<R, W, std::variant<AlternativeTypes...>, ProcessorsType> {
146145
static void write(const W& _w,
147146
const std::variant<AlternativeTypes...>& _variant,
148147
const P& _parent) {
149-
if constexpr (internal::all_fields<std::tuple<AlternativeTypes...>>()) {
148+
if constexpr (internal::all_fields_v<AlternativeTypes...>) {
150149
if constexpr (schemaful::IsSchemafulWriter<W>) {
151150
using WrappedType = rfl::Variant<
152151
NamedTuple<Field<AlternativeTypes::name_,
@@ -220,7 +219,7 @@ class ParserVariant<R, W, std::variant<AlternativeTypes...>, ProcessorsType> {
220219
*/
221220
static schema::Type to_schema(
222221
std::map<std::string, schema::Type>* _definitions) {
223-
if constexpr (internal::all_fields<std::tuple<AlternativeTypes...>>()) {
222+
if constexpr (internal::all_fields_v<AlternativeTypes...>) {
224223
return FieldVariantParser<R, W, ProcessorsType,
225224
AlternativeTypes...>::to_schema(_definitions);
226225

@@ -235,42 +234,18 @@ class ParserVariant<R, W, std::variant<AlternativeTypes...>, ProcessorsType> {
235234
_definitions);
236235

237236
} else {
238-
std::vector<schema::Type> types;
239-
build_schema(
240-
_definitions, &types,
241-
std::make_integer_sequence<int, sizeof...(AlternativeTypes)>());
242-
return schema::Type{schema::Type::AnyOf{.types_ = std::move(types)}};
237+
return schema::Type{schema::Type::AnyOf{
238+
.types_ = std::vector<schema::Type>(
239+
{one_field_to_type<AlternativeTypes>(_definitions)...})}};
243240
}
244241
}
245242

246243
private:
247-
/**
248-
* @brief Adds an alternative to the schema.
249-
*
250-
* @tparam _i The index of the alternative.
251-
* @param _definitions The map of definitions to add the schema to.
252-
* @param _types The vector of types to add the generated schema to.
253-
*/
254-
template <size_t _i>
255-
static void add_to_schema(std::map<std::string, schema::Type>* _definitions,
256-
std::vector<schema::Type>* _types) noexcept {
257-
using U = std::remove_cvref_t<
258-
std::variant_alternative_t<_i, std::variant<AlternativeTypes...>>>;
259-
_types->push_back(Parser<R, W, U, ProcessorsType>::to_schema(_definitions));
260-
}
261-
262-
/**
263-
* @brief Builds the schema for the variant.
264-
*
265-
* @tparam _is The indices of the alternatives.
266-
* @param _definitions The map of definitions to add the schema to.
267-
* @param _types The vector of types to add the generated schemas to.
268-
*/
269-
template <int... _is>
270-
static void build_schema(std::map<std::string, schema::Type>* _definitions,
271-
std::vector<schema::Type>* _types,
272-
std::integer_sequence<int, _is...>) noexcept {
273-
(add_to_schema<_is>(_definitions, _types), ...);
244+
template <class AltType>
245+
static schema::Type one_field_to_type(
246+
std::map<std::string, schema::Type>* _definitions) noexcept {
247+
return Parser<R, W, std::remove_cvref_t<AltType>,
248+
ProcessorsType>::to_schema(_definitions);
274249
}
275250

276251
/**

tests/json/test_json_schema7.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include <rfl.hpp>
2+
#include <rfl/json.hpp>
3+
4+
namespace test_json_schema7 {
5+
6+
struct Circle {
7+
double radius;
8+
};
9+
struct Rectangle {
10+
double height;
11+
double width;
12+
};
13+
14+
using Shapes = rfl::Variant<Circle, Rectangle>;
15+
16+
TEST(json, test_json_schema7) {
17+
const Shapes r = Rectangle{.height = 10, .width = 5};
18+
19+
const auto json_schema =
20+
rfl::json::to_schema<Shapes, rfl::AddTagsToVariants>();
21+
22+
ASSERT_EQ(
23+
json_schema,
24+
R"({"$schema":"https://json-schema.org/draft/2020-12/schema","anyOf":[{"type":"object","properties":{"Circle":{"$ref":"#/$defs/test_json_schema7__Circle"}},"required":["Circle"]},{"type":"object","properties":{"Rectangle":{"$ref":"#/$defs/test_json_schema7__Rectangle"}},"required":["Rectangle"]}],"$defs":{"test_json_schema7__Circle":{"type":"object","properties":{"radius":{"type":"number"}},"required":["radius"]},"test_json_schema7__Rectangle":{"type":"object","properties":{"height":{"type":"number"},"width":{"type":"number"}},"required":["height","width"]}}})");
25+
}
26+
} // namespace test_json_schema7

0 commit comments

Comments
 (0)