Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/rfl/json/schema/Type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct Type {
std::optional<std::string> description{};
std::optional<bool> deprecated{};
std::optional<std::string> deprecationMessage{};
rfl::Rename<"default", std::optional<rfl::Generic>> defaultValue{};
};

struct Boolean {
Expand Down
34 changes: 28 additions & 6 deletions include/rfl/parsing/NamedTupleParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@
#include "schema/Type.hpp"
#include "to_single_error_message.hpp"

namespace rfl {

/// forward declaration
template <class... Ps>
Generic to_generic(const auto& _t);

} // namespace rfl

namespace rfl::parsing {

template <class R, class W, bool _ignore_empty_containers, bool _all_required,
Expand Down Expand Up @@ -180,10 +188,12 @@ struct NamedTupleParser {
* @param _definitions The map of definitions to add to.
* @return The schema type.
*/
template <typename View = void>
static schema::Type to_schema(
std::map<std::string, schema::Type>* _definitions) noexcept {
std::map<std::string, schema::Type>* _definitions,
View* _view = nullptr) noexcept {
SchemaType schema;
build_schema(_definitions, &schema,
build_schema(_definitions, &schema, _view,
std::make_integer_sequence<int, size_>());
return schema::Type{schema};
}
Expand Down Expand Up @@ -226,14 +236,25 @@ struct NamedTupleParser {
}
}

template <size_t _i>
template <typename View, size_t _i>
static void add_field_to_schema(
std::map<std::string, schema::Type>* _definitions,
SchemaType* _schema) noexcept {
SchemaType* _schema,
View* _view) noexcept {
using F = internal::nth_element_t<_i, FieldTypes...>;
using U = std::remove_cvref_t<typename F::Type>;
if constexpr (!internal::is_skip_v<U> && !internal::is_extra_fields_v<U>) {
// Add default value here
auto s = Parser<R, W, U, ProcessorsType>::to_schema(_definitions);
if constexpr (!std::is_same_v<View, void>) {
s.variant_.visit([&](auto& value) {
if constexpr (std::is_same_v<std::remove_cvref_t<decltype(value)>,
schema::Type::DefaultVal>) {
value.default_value_ =
rfl::to_generic((*rfl::get<_i>(*_view)).get());
}
});
}
if constexpr (_no_field_names) {
_schema->types_.emplace_back(std::move(s));
} else {
Expand All @@ -249,11 +270,12 @@ struct NamedTupleParser {
(add_field_to_object<_is>(_w, _tup, _ptr), ...);
}

template <int... _is>
template <typename View, int... _is>
static void build_schema(std::map<std::string, schema::Type>* _definitions,
SchemaType* _schema,
View* _view,
std::integer_sequence<int, _is...>) noexcept {
(add_field_to_schema<_is>(_definitions, _schema), ...);
(add_field_to_schema<View, _is>(_definitions, _schema, _view), ...);

if constexpr (NamedTupleType::pos_extra_fields() != -1) {
using F = internal::nth_element_t<NamedTupleType::pos_extra_fields(),
Expand Down
14 changes: 11 additions & 3 deletions include/rfl/parsing/Parser_default.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,7 @@ struct Parser {

} else if constexpr (rfl::internal::is_deprecated_v<U>) {
return make_deprecated<U>(_definitions);

} else if constexpr (std::is_class_v<U> && std::is_aggregate_v<U>) {
return make_reference<U>(_definitions);

Expand Down Expand Up @@ -637,9 +638,16 @@ struct Parser {

} else {
using NamedTupleType = internal::processed_t<U, ProcessorsType>;
(*_definitions)[name] =
Parser<R, W, NamedTupleType, ProcessorsType>::to_schema(
_definitions);
if constexpr (internal::has_default_val_v<U>) {
auto t = U{};
auto view = ProcessorsType::template process<U>(to_view(t));
(*_definitions)[name] =
Parser<R, W, NamedTupleType, ProcessorsType>::to_schema(
_definitions, &view);
}else {
(*_definitions)[name] =
Parser<R, W, NamedTupleType, ProcessorsType>::to_schema(_definitions);
}
}
}
return Type{Type::Reference{name}};
Expand Down
1 change: 1 addition & 0 deletions include/rfl/parsing/schema/Type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ struct RFL_API Type {
/// using this or the Optional wrapper.
struct DefaultVal {
Ref<Type> type_;
Generic default_value_;
};

struct DescribedLiteral {
Expand Down
7 changes: 6 additions & 1 deletion src/rfl/json/to_schema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,12 @@ schema::Type type_to_json_schema_type(const parsing::schema::Type& _type,
return schema::Type{.value = schema::Type::AnyOf{.anyOf = any_of}};

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

} else if constexpr (std::is_same<T, Type::Deprecated>()) {
auto res = type_to_json_schema_type(*_t.type_, _no_required);
Expand Down
7 changes: 3 additions & 4 deletions tests/json/test_json_schema5.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,22 @@

namespace test_json_schema5 {

using Age = rfl::Validator<std::optional<unsigned int>, rfl::Minimum<0>,
rfl::Maximum<130>>;
using Age = rfl::Validator<unsigned int, rfl::Minimum<0>, rfl::Maximum<130>>;

struct Person {
rfl::Description<"Given name of this person", std::string> first_name;
rfl::Description<"Optional family name of this person",
std::optional<std::string>>
last_name;
Age age;
rfl::DefaultVal<std::string> nickname;
rfl::DefaultVal<std::string> nickname = "peter";
};

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

const std::string expected =
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"]}}})";
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"]}}})";

EXPECT_EQ(json_schema, expected);
}
Expand Down
37 changes: 37 additions & 0 deletions tests/json/test_json_schema_default_vals.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include <optional>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <string>
#include <vector>

namespace test_schema_default {

struct Config {
int port = 80;
bool autostart = true;
};

struct DefaultValField {
rfl::DefaultVal<int> with_default = 10;
};

struct DefaultWithConfig {
rfl::DefaultVal<Config> with_default = Config{443, true};
};

TEST(json, test_with_default) {
auto json_schema = rfl::json::to_schema<DefaultValField>();

std::string expected =
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":[]}}})";

EXPECT_EQ(json_schema, expected) << json_schema;

json_schema = rfl::json::to_schema<DefaultWithConfig>();
expected =
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":[]}}})";

EXPECT_EQ(json_schema, expected) << "is " << json_schema;
}

} // namespace test_schema_default