Skip to content

Commit 594707d

Browse files
autoantwortLeander Schulten
andauthored
WIP: respects DefaultVal in schema and adds default value to schema. fixes #679, #396 (#680)
Co-authored-by: Leander Schulten <Leander.Schulten@tetys.de>
1 parent af99a87 commit 594707d

7 files changed

Lines changed: 89 additions & 14 deletions

File tree

include/rfl/json/schema/Type.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <string>
77

88
#include "../../Flatten.hpp"
9+
#include "../../Generic.hpp"
910
#include "../../Literal.hpp"
1011
#include "../../Object.hpp"
1112
#include "../../Ref.hpp"
@@ -21,6 +22,7 @@ struct Type {
2122
std::optional<std::string> description{};
2223
std::optional<bool> deprecated{};
2324
std::optional<std::string> deprecationMessage{};
25+
rfl::Rename<"default", std::optional<rfl::Generic>> defaultValue{};
2426
};
2527

2628
struct Boolean {

include/rfl/parsing/NamedTupleParser.hpp

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@
3131
#include "schema/Type.hpp"
3232
#include "to_single_error_message.hpp"
3333

34+
namespace rfl {
35+
36+
/// forward declaration
37+
template <class... Ps>
38+
Generic to_generic(const auto& _t);
39+
40+
} // namespace rfl
41+
3442
namespace rfl::parsing {
3543

3644
template <class R, class W, bool _ignore_empty_containers, bool _all_required,
@@ -180,10 +188,12 @@ struct NamedTupleParser {
180188
* @param _definitions The map of definitions to add to.
181189
* @return The schema type.
182190
*/
191+
template <typename View = void>
183192
static schema::Type to_schema(
184-
std::map<std::string, schema::Type>* _definitions) noexcept {
193+
std::map<std::string, schema::Type>* _definitions,
194+
View* _view = nullptr) noexcept {
185195
SchemaType schema;
186-
build_schema(_definitions, &schema,
196+
build_schema(_definitions, &schema, _view,
187197
std::make_integer_sequence<int, size_>());
188198
return schema::Type{schema};
189199
}
@@ -226,14 +236,25 @@ struct NamedTupleParser {
226236
}
227237
}
228238

229-
template <size_t _i>
239+
template <typename View, size_t _i>
230240
static void add_field_to_schema(
231241
std::map<std::string, schema::Type>* _definitions,
232-
SchemaType* _schema) noexcept {
242+
SchemaType* _schema,
243+
View* _view) noexcept {
233244
using F = internal::nth_element_t<_i, FieldTypes...>;
234245
using U = std::remove_cvref_t<typename F::Type>;
235246
if constexpr (!internal::is_skip_v<U> && !internal::is_extra_fields_v<U>) {
247+
// Add default value here
236248
auto s = Parser<R, W, U, ProcessorsType>::to_schema(_definitions);
249+
if constexpr (!std::is_same_v<View, void>) {
250+
s.variant_.visit([&](auto& value) {
251+
if constexpr (std::is_same_v<std::remove_cvref_t<decltype(value)>,
252+
schema::Type::DefaultVal>) {
253+
value.default_value_ =
254+
rfl::to_generic((*rfl::get<_i>(*_view)).get());
255+
}
256+
});
257+
}
237258
if constexpr (_no_field_names) {
238259
_schema->types_.emplace_back(std::move(s));
239260
} else {
@@ -249,11 +270,12 @@ struct NamedTupleParser {
249270
(add_field_to_object<_is>(_w, _tup, _ptr), ...);
250271
}
251272

252-
template <int... _is>
273+
template <typename View, int... _is>
253274
static void build_schema(std::map<std::string, schema::Type>* _definitions,
254275
SchemaType* _schema,
276+
View* _view,
255277
std::integer_sequence<int, _is...>) noexcept {
256-
(add_field_to_schema<_is>(_definitions, _schema), ...);
278+
(add_field_to_schema<View, _is>(_definitions, _schema, _view), ...);
257279

258280
if constexpr (NamedTupleType::pos_extra_fields() != -1) {
259281
using F = internal::nth_element_t<NamedTupleType::pos_extra_fields(),

include/rfl/parsing/Parser_default.hpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,7 @@ struct Parser {
572572

573573
} else if constexpr (rfl::internal::is_deprecated_v<U>) {
574574
return make_deprecated<U>(_definitions);
575+
575576
} else if constexpr (std::is_class_v<U> && std::is_aggregate_v<U>) {
576577
return make_reference<U>(_definitions);
577578

@@ -637,9 +638,16 @@ struct Parser {
637638

638639
} else {
639640
using NamedTupleType = internal::processed_t<U, ProcessorsType>;
640-
(*_definitions)[name] =
641-
Parser<R, W, NamedTupleType, ProcessorsType>::to_schema(
642-
_definitions);
641+
if constexpr (internal::has_default_val_v<U>) {
642+
auto t = U{};
643+
auto view = ProcessorsType::template process<U>(to_view(t));
644+
(*_definitions)[name] =
645+
Parser<R, W, NamedTupleType, ProcessorsType>::to_schema(
646+
_definitions, &view);
647+
}else {
648+
(*_definitions)[name] =
649+
Parser<R, W, NamedTupleType, ProcessorsType>::to_schema(_definitions);
650+
}
643651
}
644652
}
645653
return Type{Type::Reference{name}};

include/rfl/parsing/schema/Type.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <string>
77
#include <vector>
88

9+
#include "../../Generic.hpp"
910
#include "../../Object.hpp"
1011
#include "../../Ref.hpp"
1112
#include "../../Variant.hpp"
@@ -61,6 +62,7 @@ struct RFL_API Type {
6162
/// using this or the Optional wrapper.
6263
struct DefaultVal {
6364
Ref<Type> type_;
65+
Generic default_value_;
6466
};
6567

6668
struct DescribedLiteral {

src/rfl/json/to_schema.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,12 @@ schema::Type type_to_json_schema_type(const parsing::schema::Type& _type,
223223
return schema::Type{.value = schema::Type::AnyOf{.anyOf = any_of}};
224224

225225
} else if constexpr (std::is_same<T, Type::DefaultVal>()) {
226-
return type_to_json_schema_type(*_t.type_, _no_required);
226+
auto res = type_to_json_schema_type(*_t.type_, _no_required);
227+
const auto update_prediction = [&](auto _v) -> schema::Type {
228+
_v.annotations.value_.defaultValue = _t.default_value_;
229+
return schema::Type{_v};
230+
};
231+
return rfl::visit(update_prediction, res.value);
227232

228233
} else if constexpr (std::is_same<T, Type::Deprecated>()) {
229234
auto res = type_to_json_schema_type(*_t.type_, _no_required);

tests/json/test_json_schema5.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,22 @@
55

66
namespace test_json_schema5 {
77

8-
using Age = rfl::Validator<std::optional<unsigned int>, rfl::Minimum<0>,
9-
rfl::Maximum<130>>;
8+
using Age = rfl::Validator<unsigned int, rfl::Minimum<0>, rfl::Maximum<130>>;
109

1110
struct Person {
1211
rfl::Description<"Given name of this person", std::string> first_name;
1312
rfl::Description<"Optional family name of this person",
1413
std::optional<std::string>>
1514
last_name;
1615
Age age;
17-
rfl::DefaultVal<std::string> nickname;
16+
rfl::DefaultVal<std::string> nickname = "peter";
1817
};
1918

2019
TEST(json, test_json_schema5) {
2120
const auto json_schema = rfl::json::to_schema<Person>();
2221

2322
const std::string expected =
24-
R"({"$schema":"https://json-schema.org/draft/2020-12/schema","$ref":"#/$defs/test_json_schema5__Person","$defs":{"test_json_schema5__Person":{"type":"object","properties":{"first_name":{"type":"string","description":"Given name of this person"},"last_name":{"description":"Optional family name of this person","anyOf":[{"type":"string"},{"type":"null"}]},"age":{"allOf":[{"minimum":0,"type":"number"},{"maximum":130,"type":"number"}]},"nickname":{"type":"string"}},"required":["first_name"]}}})";
23+
R"({"$schema":"https://json-schema.org/draft/2020-12/schema","$ref":"#/$defs/test_json_schema5__Person","$defs":{"test_json_schema5__Person":{"type":"object","properties":{"first_name":{"type":"string","description":"Given name of this person"},"last_name":{"description":"Optional family name of this person","anyOf":[{"type":"string"},{"type":"null"}]},"age":{"allOf":[{"minimum":0,"type":"integer"},{"maximum":130,"type":"integer"}]},"nickname":{"type":"string","default":"peter"}},"required":["first_name","age"]}}})";
2524

2625
EXPECT_EQ(json_schema, expected);
2726
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include <optional>
2+
#include <rfl.hpp>
3+
#include <rfl/json.hpp>
4+
#include <string>
5+
#include <vector>
6+
7+
namespace test_schema_default {
8+
9+
struct Config {
10+
int port = 80;
11+
bool autostart = true;
12+
};
13+
14+
struct DefaultValField {
15+
rfl::DefaultVal<int> with_default = 10;
16+
};
17+
18+
struct DefaultWithConfig {
19+
rfl::DefaultVal<Config> with_default = Config{443, true};
20+
};
21+
22+
TEST(json, test_with_default) {
23+
auto json_schema = rfl::json::to_schema<DefaultValField>();
24+
25+
std::string expected =
26+
R"({"$schema":"https://json-schema.org/draft/2020-12/schema","$ref":"#/$defs/test_schema_default__DefaultValField","$defs":{"test_schema_default__DefaultValField":{"type":"object","properties":{"with_default":{"type":"integer","default":10}},"required":[]}}})";
27+
28+
EXPECT_EQ(json_schema, expected) << json_schema;
29+
30+
json_schema = rfl::json::to_schema<DefaultWithConfig>();
31+
expected =
32+
R"({"$schema":"https://json-schema.org/draft/2020-12/schema","$ref":"#/$defs/test_schema_default__DefaultWithConfig","$defs":{"test_schema_default__Config":{"type":"object","properties":{"port":{"type":"integer"},"autostart":{"type":"boolean"}},"required":["port","autostart"]},"test_schema_default__DefaultWithConfig":{"type":"object","properties":{"with_default":{"$ref":"#/$defs/test_schema_default__Config","default":{"port":443,"autostart":true}}},"required":[]}}})";
33+
34+
EXPECT_EQ(json_schema, expected) << "is " << json_schema;
35+
}
36+
37+
} // namespace test_schema_default

0 commit comments

Comments
 (0)