29 #include <type_traits>
36 namespace fable::schema {
38 template <
typename T,
size_t N,
typename P>
41 using Type = std::array<T, N>;
42 using PrototypeSchema = P;
44 Array(Type* ptr, std::string desc)
47 Array(Type* ptr, PrototypeSchema prototype)
50 Array(Type* ptr, PrototypeSchema prototype, std::string desc)
59 [[nodiscard]]
bool require_all()
const {
return option_require_all_; }
65 option_require_all_ = value;
66 this->type_ = value ? JsonType::array : JsonType::null;
74 return std::move(*
this);
78 [[nodiscard]] std::string
type_string()
const override {
return "array of " + prototype_.type_string(); }
82 if (option_require_all_) {
83 j = this->json_schema_array();
86 {
"oneOf", Json::array({
87 this->json_schema_array(),
88 this->json_schema_object(),
93 this->augment_schema(j);
97 bool validate(
const Conf& c, std::optional<SchemaError>& err)
const override {
98 if (option_require_all_) {
103 if (!this->validate_array(c, err)) {
111 case JsonType::array:
112 return this->validate_array(c, err);
113 case JsonType::object:
114 return this->validate_object(c, err);
116 return this->set_wrong_type(err, c);
122 assert(ptr_ !=
nullptr);
123 j = serialize(*ptr_);
127 assert(ptr_ !=
nullptr);
128 assert(c->type() == JsonType::array || c->type() == JsonType::object);
130 this->deserialize_into(c, *ptr_);
133 [[nodiscard]]
Json serialize(
const Type& xs)
const {
134 Json j = Json::array();
143 assert(j.type() == JsonType::array);
144 for (
const auto& x : v) {
145 j.emplace_back(prototype_.serialize(x));
157 this->deserialize_into(c, array);
161 void deserialize_into(
const Conf& c, Type& v)
const {
162 if (c->type() == JsonType::array) {
163 this->deserialize_from_array(v, c);
164 }
else if (c->type() == JsonType::object) {
165 this->deserialize_from_object(v, c);
167 throw this->wrong_type(c);
174 [[nodiscard]]
Json json_schema_array()
const {
175 return Json::object({
177 {
"items", prototype_.json_schema()},
183 [[nodiscard]]
Json json_schema_object()
const {
184 return Json::object({
186 {
"additionalProperties",
false},
187 {
"patternProperties",
191 {
"type", prototype_.json_schema()},
207 bool validate_array(
const Conf& c, std::optional<SchemaError>& err)
const {
208 assert(c->type() == JsonType::array);
209 if (c->size() != N) {
210 return this->set_error(err, c,
"require exactly {} items in array, got {}", N, c->size());
212 for (
const auto& x : c.to_array()) {
213 if (!prototype_.validate(x, err)) {
234 bool validate_object(
const Conf& c, std::optional<SchemaError>& err)
const {
235 assert(c->type() == JsonType::object);
236 for (
const auto& kv : c->items()) {
237 const auto& key = kv.key();
239 std::ignore = this->parse_index(key);
240 }
catch (std::exception& e) {
241 return this->set_error(err, c, e.what());
243 if (prototype_.validate(c.at(key), err)) {
262 [[nodiscard]]
size_t parse_index(
const std::string& s)
const {
266 throw std::invalid_argument(
"invalid index key in object, require integer, got ''");
268 if (s.size() > 1 && s[0] ==
'0') {
269 throw std::invalid_argument(fmt::format(
"invalid index key in object, require base-10 value, got '{}'", s));
272 if (ch < '0' || ch >
'9') {
273 throw std::invalid_argument(fmt::format(
"invalid index key in object, require integer, got '{}'", s));
276 size_t idx = std::stoul(s);
278 throw std::invalid_argument(fmt::format(
"out-of-range index key in object, require < {}, got '{}'", N, s));
283 [[nodiscard]]
size_t parse_index(
const Conf& c,
const std::string& s)
const {
285 return parse_index(s);
286 }
catch (std::exception& e) {
287 throw this->error(c, e.what());
291 [[nodiscard]] SchemaError wrong_type(
const Conf& c)
const {
292 std::string got = to_string(c->type());
293 return this->error(c,
"property must have type array or object, got {}", got);
296 void deserialize_from_object(Type& array,
const Conf& c)
const {
297 for (
const auto& kv : c->items()) {
298 const auto& key = kv.key();
299 size_t idx = this->parse_index(c, key);
300 prototype_.deserialize_into(c.at(key), array[idx]);
304 void deserialize_from_array(Type& array,
const Conf& c)
const {
305 auto src = c.to_array();
306 size_t n = src.size();
308 for (
size_t i = 0; i < n; i++) {
309 array[i] = prototype_.deserialize(src[i]);
314 bool option_require_all_{
false};
315 PrototypeSchema prototype_{};
319 template <
typename T,
typename P,
size_t N>
320 Array<T, N, P> make_schema(std::array<T, N>* ptr, P prototype, std::string desc) {
321 return Array<T, N, P>(ptr, std::move(prototype), std::move(desc));
324 template <
typename T,
size_t N>
325 Array<T, N, decltype(make_prototype<T>())> make_schema(std::array<T, N>* ptr, std::string desc) {
326 return Array<T, N, decltype(make_prototype<T>())>(ptr, std::move(desc));
void reset_ptr() override
Definition: array.hpp:171
Type deserialize(const Conf &c) const
Definition: array.hpp:155
void from_conf(const Conf &c) override
Definition: array.hpp:126
bool validate(const Conf &c, std::optional< SchemaError > &err) const override
Definition: array.hpp:97
Json json_schema() const override
Definition: array.hpp:80
void to_json(Json &j) const override
Definition: array.hpp:121
bool require_all() const
Definition: array.hpp:59
void serialize_into(Json &j, const Type &v) const
Definition: array.hpp:142
Array< T, N, P > require_all(bool value) &&
Definition: array.hpp:72
void set_require_all(bool value)
Definition: array.hpp:64
std::string type_string() const override
Definition: array.hpp:78
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