36 namespace fable::schema {
46 template <
typename T,
typename P>
49 using Type = std::map<std::string, T>;
50 using PrototypeSchema = std::remove_cv_t<std::remove_reference_t<P>>;
52 Map(Type* ptr, std::string desc) :
Map(ptr, make_prototype<T>(), std::move(desc)) {}
54 Map(Type* ptr, PrototypeSchema prototype)
55 :
Base<Map<T, P>>(JsonType::object), prototype_(std::move(prototype)), ptr_(ptr) {
56 prototype_.reset_ptr();
59 Map(Type* ptr, PrototypeSchema prototype, std::string desc)
60 :
Base<Map<T, P>>(JsonType::object, std::move(desc)), prototype_(std::move(prototype)), ptr_(ptr) {
61 prototype_.reset_ptr();
65 [[nodiscard]]
bool unique_properties()
const {
return unique_properties_; }
66 [[nodiscard]]
Map<T, P> unique_properties(
bool value) && {
67 set_unique_properties(value);
68 return std::move(*
this);
70 void set_unique_properties(
bool value) {
71 unique_properties_ = value;
74 [[nodiscard]]
const std::vector<std::string>& required_properties()
const {
return required_; }
75 [[nodiscard]]
Map<T, P> require_properties(
const std::vector<std::string>& values) && {
76 set_required_properties(values);
77 return std::move(*
this);
79 [[nodiscard]]
Map<T, P> require_property(
const std::string& value) && {
80 add_required_property(value);
81 return std::move(*
this);
83 void set_required_properties(
const std::vector<std::string>& values) {
86 void add_required_property(
const std::string& value) {
87 required_.emplace_back(value);
90 [[nodiscard]]
const std::string& pattern()
const {
return pattern_; }
91 [[nodiscard]]
Map<T, P> pattern(
const std::string& value) && {
93 return std::move(*
this);
95 void set_pattern(
const std::string& value) {
103 {
"additionalProperties", prototype_.json_schema()},
105 if (!required_.empty()) {
106 j[
"required"] = required_;
108 if (min_properties_ != 0) {
109 j[
"minProperties"] = min_properties_;
111 if (max_properties_ != std::numeric_limits<size_t>::max()) {
112 j[
"maxProperties"] = max_properties_;
114 if (!pattern_.empty()) {
115 j[
"propertyNames"][
"pattern"] = pattern_;
117 this->augment_schema(j);
121 bool validate(
const Conf& c, std::optional<SchemaError>& err)
const override {
126 if (c->size() < min_properties_) {
127 return this->set_error(err, c,
"expect at least {} properties, got {}", max_properties_, c->size());
129 assert(required_.size() <= max_properties_);
130 if (c->size() > max_properties_) {
131 return this->set_error(err, c,
"expect at most {} properties, got {}", max_properties_, c->size());
134 for (
const auto& k : required_) {
136 return this->set_error(err, c,
"missing property: {}", k);
140 std::optional<std::regex> pattern;
141 if (!pattern_.empty()) {
143 pattern.emplace(pattern_);
144 }
catch (std::regex_error& e) {
147 return this->set_error(err, c,
"invalid regex '{}': {}", pattern_, e.what());
150 for (
const auto& kv : c->items()) {
151 if (!prototype_.validate(c.
at(kv.key()), err)) {
154 if (pattern && !std::regex_match(kv.key(), *pattern)) {
155 return this->set_error(err, c,
"expect property name to match regex '{}': {}", pattern_, kv.key());
163 assert(ptr_ !=
nullptr);
164 j = serialize(*ptr_);
168 assert(ptr_ !=
nullptr);
169 for (
const auto& i : c->items()) {
170 const auto& key = i.key();
171 if (unique_properties_ && ptr_->count(key)) {
172 throw this->error(c,
"key {} has already been defined", key);
174 ptr_->insert(std::make_pair(key, deserialize_item(c, key)));
178 [[nodiscard]]
Json serialize(
const Type& x)
const {
180 serialize_into(j, x);
184 [[nodiscard]] Type deserialize(
const Conf& c)
const {
186 deserialize_into(c, tmp);
190 void serialize_into(Json& j,
const Type& x)
const {
191 for (
const auto& kv : x) {
192 j[kv.first] = prototype_.serialize(kv.second);
196 void deserialize_into(
const Conf& c, Type& x)
const {
197 for (
const auto& i : c->items()) {
198 const auto& key = i.key();
199 x.insert(std::make_pair(key, deserialize_item(c, key)));
203 [[nodiscard]] T deserialize_item(
const Conf& c,
const std::string& key)
const {
204 return prototype_.deserialize(c.at(key));
210 bool unique_properties_{
true};
211 size_t min_properties_{0};
212 size_t max_properties_{std::numeric_limits<size_t>::max()};
213 std::string pattern_{};
214 std::vector<std::string> required_{};
215 PrototypeSchema prototype_{};
219 template <
typename T,
typename P,
typename S>
220 Map<T, P> make_schema(std::map<std::string, T>* ptr, P&& prototype, S&& desc) {
221 return {ptr, std::forward<P>(prototype), std::forward<S>(desc)};
224 template <
typename T,
typename S>
225 Map<T, decltype(make_prototype<T>())> make_schema(std::map<std::string, T>* ptr, S&& desc) {
226 return {ptr, std::forward<S>(desc)};
bool has(const std::string &key) const
Definition: conf.hpp:165
Conf at(const std::string &key) const
Definition: conf.cpp:58
Definition: interface.hpp:398
bool validate_type(const Conf &c, std::optional< SchemaError > &err) const
Definition: interface.hpp:459
virtual Json to_json() const
Definition: interface.hpp:254
void reset_ptr() override
Definition: map.hpp:207
void to_json(Json &j) const override
Definition: map.hpp:162
void from_conf(const Conf &c) override
Definition: map.hpp:167
Json json_schema() const override
Definition: map.hpp:100
bool validate(const Conf &c, std::optional< SchemaError > &err) const override
Definition: map.hpp:121
nlohmann::json Json
Definition: fable_fwd.hpp:35