29 #include <type_traits>
36 namespace fable::schema {
95 template <
typename T,
size_t N,
typename P>
98 using Type = std::array<T, N>;
99 using PrototypeSchema = P;
101 Array(Type* ptr, std::string desc) :
Array<T, N, P>(ptr, make_prototype<T>(), std::move(desc)) {}
103 Array(Type* ptr, PrototypeSchema prototype)
106 Array(Type* ptr, PrototypeSchema prototype, std::string desc)
116 [[nodiscard]]
bool require_all()
const {
return option_require_all_; }
136 option_require_all_ = value;
137 this->type_ = value ? JsonType::array : JsonType::null;
147 return std::move(*
this);
152 return "array of " + prototype_.type_string();
157 if (option_require_all_) {
158 j = this->json_schema_array();
161 {
"oneOf", Json::array({
162 this->json_schema_array(),
163 this->json_schema_object(),
168 this->augment_schema(j);
172 bool validate(
const Conf& c, std::optional<SchemaError>& err)
const override {
173 if (option_require_all_) {
178 if (!this->validate_array(c, err)) {
186 case JsonType::array:
187 return this->validate_array(c, err);
188 case JsonType::object:
189 return this->validate_object(c, err);
191 return this->set_wrong_type(err, c);
198 assert(ptr_ !=
nullptr);
199 j = serialize(*ptr_);
203 assert(ptr_ !=
nullptr);
204 assert(c->type() == JsonType::array || c->type() == JsonType::object);
206 this->deserialize_into(c, *ptr_);
209 [[nodiscard]]
Json serialize(
const Type& xs)
const {
210 Json j = Json::array();
219 assert(j.type() == JsonType::array);
220 for (
size_t i = 0; i < N; i++) {
221 j.emplace_back(prototype_.serialize(v[i]));
233 this->deserialize_into(c, array);
237 void deserialize_into(
const Conf& c, Type& v)
const {
238 if (c->type() == JsonType::array) {
239 this->deserialize_from_array(v, c);
240 }
else if (c->type() == JsonType::object) {
241 this->deserialize_from_object(v, c);
243 throw this->wrong_type(c);
250 [[nodiscard]]
Json json_schema_array()
const {
251 return Json::object({
253 {
"items", prototype_.json_schema()},
259 [[nodiscard]]
Json json_schema_object()
const {
260 return Json::object({
262 {
"additionalProperties",
false},
263 {
"patternProperties",
267 {
"type", prototype_.json_schema()},
283 bool validate_array(
const Conf& c, std::optional<SchemaError>& err)
const {
284 assert(c->type() == JsonType::array);
285 if (c->size() != N) {
286 return this->set_error(err, c,
"require exactly {} items in array, got {}", N, c->size());
288 for (
const auto& x : c.to_array()) {
289 if (!prototype_.validate(x, err)) {
317 bool validate_object(
const Conf& c, std::optional<SchemaError>& err)
const {
318 assert(c->type() == JsonType::object);
319 for (
const auto& kv : c->items()) {
320 const auto& key = kv.key();
322 std::ignore = this->parse_index(key);
323 }
catch (std::exception& e) {
324 return this->set_error(err, c, e.what());
326 if (prototype_.validate(c.at(key), err)) {
345 [[nodiscard]]
size_t parse_index(
const std::string& s)
const {
349 throw std::invalid_argument(
"invalid index key in object, require integer, got ''");
351 if (s.size() > 1 && s[0] ==
'0') {
352 throw std::invalid_argument(
353 fmt::format(
"invalid index key in object, require base-10 value, got '{}'", s));
356 if (ch < '0' || ch >
'9') {
357 throw std::invalid_argument(
358 fmt::format(
"invalid index key in object, require integer, got '{}'", s));
361 size_t idx = std::stoul(s);
363 throw std::invalid_argument(
364 fmt::format(
"out-of-range index key in object, require < {}, got '{}'", N, s));
369 [[nodiscard]]
size_t parse_index(
const Conf& c,
const std::string& s)
const {
371 return parse_index(s);
372 }
catch (std::exception& e) {
373 throw this->error(c, e.what());
377 [[nodiscard]] SchemaError wrong_type(
const Conf& c)
const {
378 std::string got = to_string(c->type());
379 return this->error(c,
"property must have type array or object, got {}", got);
382 void deserialize_from_object(Type& array,
const Conf& c)
const {
383 for (
const auto& kv : c->items()) {
384 const auto& key = kv.key();
385 size_t idx = this->parse_index(c, key);
386 prototype_.deserialize_into(c.at(key), array[idx]);
390 void deserialize_from_array(Type& array,
const Conf& c)
const {
391 auto src = c.to_array();
392 if (src.size() != N) {
393 throw this->error(c,
"require exactly {} items in array, got {}", N, c->size());
395 for (
size_t i = 0; i < N; i++) {
396 array[i] = prototype_.deserialize(src[i]);
401 bool option_require_all_{
false};
402 PrototypeSchema prototype_{};
406 template <
typename T,
typename P,
size_t N,
typename S>
407 Array<T, N, P> make_schema(std::array<T, N>* ptr, P&& prototype, S&& desc) {
408 return {ptr, std::forward<P>(prototype), std::forward<S>(desc)};
411 template <
typename T,
size_t N,
typename S>
412 Array<T, N, decltype(make_prototype<T>())> make_schema(std::array<T, N>* ptr, S&& desc) {
413 return {ptr, std::forward<S>(desc)};
void reset_ptr() override
Definition: array.hpp:247
Type deserialize(const Conf &c) const
Definition: array.hpp:231
void from_conf(const Conf &c) override
Definition: array.hpp:202
bool validate(const Conf &c, std::optional< SchemaError > &err) const override
Definition: array.hpp:172
Json json_schema() const override
Definition: array.hpp:155
void to_json(Json &j) const override
Definition: array.hpp:197
bool require_all() const
Definition: array.hpp:116
void serialize_into(Json &j, const Type &v) const
Definition: array.hpp:218
Array< T, N, P > require_all(bool value) &&
Definition: array.hpp:145
void set_require_all(bool value)
Definition: array.hpp:135
std::string type_string() const override
Definition: array.hpp:151
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
nlohmann::json Json
Definition: fable_fwd.hpp:35