36 namespace fable::schema {
46 template <
typename T,
typename P>
49 using Type = std::map<std::string, T>;
50 using PrototypeSchema = 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)
61 , prototype_(std::move(prototype))
63 prototype_.reset_ptr();
67 [[nodiscard]]
bool unique_properties()
const {
return unique_properties_; }
68 Map<T, P> unique_properties(
bool value) && {
69 unique_properties_ = value;
70 return std::move(*
this);
73 [[nodiscard]]
const std::vector<std::string>& required()
const {
return required_; }
74 Map<T, P> require_properties(
const std::vector<std::string>& values) && {
76 return std::move(*
this);
78 Map<T, P> require_property(
const std::string& value) && {
79 required_.emplace_back(value);
80 return std::move(*
this);
83 [[nodiscard]]
const std::string& pattern()
const {
return pattern_; }
84 Map<T, P> pattern(
const std::string& value) && {
86 return std::move(*
this);
93 {
"additionalProperties", prototype_.json_schema()},
95 if (!required_.empty()) {
96 j[
"required"] = required_;
98 if (min_properties_ != 0) {
99 j[
"minProperties"] = min_properties_;
101 if (max_properties_ != std::numeric_limits<size_t>::max()) {
102 j[
"maxProperties"] = max_properties_;
104 if (!pattern_.empty()) {
105 j[
"propertyNames"][
"pattern"] = pattern_;
107 this->augment_schema(j);
111 bool validate(
const Conf& c, std::optional<SchemaError>& err)
const override {
116 if (c->size() < min_properties_) {
117 return this->set_error(err, c,
"expect at least {} properties, got {}", max_properties_, c->size());
119 assert(required_.size() <= max_properties_);
120 if (c->size() > max_properties_) {
121 return this->set_error(err, c,
"expect at most {} properties, got {}", max_properties_, c->size());
124 for (
const auto& k : required_) {
126 return this->set_error(err, c,
"missing property: {}", k);
130 std::optional<std::regex> pattern;
131 if (!pattern_.empty()) {
133 *pattern = std::regex(pattern_);
134 }
catch (std::regex_error& e) {
137 return this->set_error(err, c,
"invalid regex '{}': {}", pattern_, e.what());
140 for (
const auto& kv : c->items()) {
141 if (!prototype_.validate(c.
at(kv.key()), err)) {
144 if (pattern && !std::regex_match(kv.key(), *pattern)) {
145 return this->set_error(err, c,
"expect property name to match regex '{}': {}", pattern_, kv.key());
153 assert(ptr_ !=
nullptr);
154 j = serialize(*ptr_);
158 assert(ptr_ !=
nullptr);
159 for (
const auto& i : c->items()) {
160 const auto& key = i.key();
161 if (unique_properties_ && ptr_->count(key)) {
162 throw this->error(c,
"key {} has already been defined", key);
164 ptr_->insert(std::make_pair(key, deserialize_item(c, key)));
168 [[nodiscard]]
Json serialize(
const Type& x)
const {
170 serialize_into(j, x);
174 [[nodiscard]] Type deserialize(
const Conf& c)
const {
176 deserialize_into(c, tmp);
180 void serialize_into(Json& j,
const Type& x)
const {
181 for (
const auto& kv : x) {
182 j[kv.first] = prototype_.serialize(kv.second);
186 void deserialize_into(
const Conf& c, Type& x)
const {
187 for (
const auto& i : c->items()) {
188 const auto& key = i.key();
189 x.insert(std::make_pair(key, deserialize_item(c, key)));
193 T deserialize_item(
const Conf& c,
const std::string& key)
const {
194 return prototype_.deserialize(c.at(key));
200 bool unique_properties_{
true};
201 size_t min_properties_{0};
202 size_t max_properties_{std::numeric_limits<size_t>::max()};
203 std::string pattern_{};
204 std::vector<std::string> required_{};
205 PrototypeSchema prototype_{};
209 template <
typename T,
typename P>
210 Map<T, P> make_schema(std::map<std::string, T>* ptr, P prototype, std::string desc) {
211 return Map<T, P>(ptr, std::move(prototype), std::move(desc));
214 template <
typename T>
215 Map<T, decltype(make_prototype<T>())> make_schema(std::map<std::string, T>* ptr, std::string desc) {
216 return Map<T, decltype(make_prototype<T>())>(ptr, std::move(desc));
bool has(const std::string &key) const
Definition: conf.hpp:132
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:197
void to_json(Json &j) const override
Definition: map.hpp:152
void from_conf(const Conf &c) override
Definition: map.hpp:157
Json json_schema() const override
Definition: map.hpp:90
bool validate(const Conf &c, std::optional< SchemaError > &err) const override
Definition: map.hpp:111
nlohmann::json Json
Definition: fable_fwd.hpp:35