1 #ifndef GARLIC_CONSTRAINTS_H
2 #define GARLIC_CONSTRAINTS_H
7 #include <unordered_set>
22 enum flags : uint8_t {
40 inline bool is_valid() const noexcept {
return flag & flags::valid; }
43 inline bool is_field() const noexcept {
return flag & flags::field; }
45 inline void set_valid() noexcept { flag =
static_cast<flags
>(flag & flags::valid); }
46 inline void set_field() noexcept { flag =
static_cast<flags
>(flag & flags::field); }
49 inline operator bool()
const {
return is_valid(); }
51 template<flags Flag = flags::none>
56 .name = std::move(
name),
57 .reason = std::move(
reason),
62 static inline ConstraintResult leaf_field_failure(
63 text&&
name, text&&
reason=text::no_text()) noexcept {
64 return leaf_failure<flags::field>(std::move(
name), std::move(
reason));
67 static inline ConstraintResult
70 ConstraintResult&& inner_detail,
71 text&&
reason = text::no_text()) noexcept {
72 sequence<ConstraintResult>
details(1);
73 details.push_back(std::move(inner_detail));
74 return ConstraintResult {
76 .name = std::move(
name),
77 .reason = std::move(
reason),
82 static ConstraintResult ok() noexcept {
83 return ConstraintResult {
84 .details = sequence<ConstraintResult>::no_sequence(),
85 .name = text::no_text(),
86 .reason = text::no_text(),
93 class constraint_context {
95 enum flags : uint8_t {
102 flags flag = flags::none;
105 text&& name = text::no_text(),
106 text&& message = text::no_text(),
108 ) : name(std::move(name)), message(std::move(message)), flag(fatal ? flags::fatal : flags::none) {}
110 virtual ~constraint_context() =
default;
112 inline bool is_fatal() const noexcept {
return flag & flags::fatal; }
114 template<
bool Field = false,
bool Leaf = true>
115 auto fail() const noexcept -> ConstraintResult {
116 return ConstraintResult {
117 .details = (Leaf ? sequence<ConstraintResult>::no_sequence() : sequence<ConstraintResult>()),
120 .flag = (Field ? ConstraintResult::flags::field : ConstraintResult::flags::none)
124 template<
bool Field = false,
bool Leaf = true>
125 auto fail(
const text& message)
const noexcept -> ConstraintResult {
126 if (this->message.empty())
127 return ConstraintResult {
128 .details = (Leaf ? sequence<ConstraintResult>::no_sequence() : sequence<ConstraintResult>()),
131 .flag = (Field ? ConstraintResult::flags::field : ConstraintResult::flags::none)
133 return this->fail<Field, Leaf>();
136 template<
bool Field = false>
137 auto fail(
const text& message, sequence<ConstraintResult>&& details)
const noexcept {
138 return ConstraintResult {
139 .details = std::move(details),
141 .reason = (this->message.empty() ? message : this->message),
142 .flag = (Field ? ConstraintResult::flags::field : ConstraintResult::flags::none)
146 template<
bool Field = false>
147 auto fail(
const text& message, ConstraintResult&& inner_detail)
const noexcept {
148 sequence<ConstraintResult> details(1);
149 details.push_back(std::move(inner_detail));
150 return this->fail<Field>(message, std::move(details));
153 inline auto ok() const noexcept -> ConstraintResult {
154 return ConstraintResult::ok();
158 template<GARLIC_VIEW Layer>
160 get_text(Layer&& layer,
const char* key, text default_value) noexcept {
161 get_member(layer, key, [&default_value](
const auto& result) {
162 default_value = text::copy(result.get_string_view());
164 return default_value;
168 using constraint_test_handler = ConstraintResult (*)(
const T&,
const constraint_context*);
171 using constraint_quick_test_handler = bool (*)(
const T&,
const constraint_context*);
174 template<
typename InnerTag>
176 template<GARLIC_VIEW Layer>
177 static inline ConstraintResult
178 test(
const Layer& layer,
const constraint_context* context) noexcept {
179 return InnerTag::test(layer, *
reinterpret_cast<const typename InnerTag::context_type*
>(context));
182 template<GARLIC_VIEW Layer>
184 quick_test(
const Layer& layer,
const constraint_context* context) noexcept {
185 return InnerTag::quick_test(layer, *
reinterpret_cast<const typename InnerTag::context_type*
>(context));
189 template<
typename Tag,
typename... Rest>
191 using tag = tag_wrapper<Tag>;
192 using rest = registry<Rest...>;
193 static constexpr
unsigned id =
sizeof...(Rest);
194 static constexpr
unsigned not_found = UINT32_MAX;
196 template<
typename Query>
197 static unsigned constexpr position_of() {
198 if (std::is_same<Tag, Query>::value)
200 constexpr
auto result = rest::template position_of<Query>();
201 if (result == not_found)
206 template<
typename Query, GARLIC_VIEW Layer>
207 static constexpr
auto test_handler() {
208 if (std::is_same<Tag, Query>::value)
209 return &tag::template test<Layer>;
210 return rest::template test_handler<Query, Layer>();
213 template<GARLIC_VIEW Layer>
214 static constexpr std::array<constraint_test_handler<Layer>,
sizeof...(Rest) + 1>
216 return {&tag::template test<Layer>, &tag_wrapper<Rest>::template test<Layer>...};
219 template<GARLIC_VIEW Layer>
220 static constexpr std::array<constraint_quick_test_handler<Layer>,
sizeof...(Rest) + 1>
221 quick_test_handlers() {
222 return {&tag::template quick_test<Layer>, &tag_wrapper<Rest>::template quick_test<Layer>...};
226 template<
typename Tag>
227 struct registry<Tag> {
228 using tag = tag_wrapper<Tag>;
229 static constexpr
unsigned id = 0;
230 static constexpr
unsigned not_found = UINT32_MAX;
232 template<
typename Query>
233 static unsigned constexpr position_of() {
234 return (std::is_same<Tag, Query>::value ? 0 : not_found);
237 template<
typename Query, GARLIC_VIEW Layer>
238 static constexpr
auto test_handler() {
239 return (std::is_same<Tag, Query>::value ? &tag::template test<Layer> :
nullptr);
242 template<
typename Query, GARLIC_VIEW Layer>
243 static constexpr
auto quick_test_handler() {
244 return (std::is_same<Tag, Query>::value ? &tag::template quick_test<Layer> :
nullptr);
277 using context_pointer = std::shared_ptr<constraint_context>;
278 context_pointer context_;
281 Constraint(
unsigned index, context_pointer&& context) : context_(std::move(context)), index_(index) {}
289 template<
typename Tag,
typename... Args>
290 static Constraint make(Args&&... args) noexcept;
298 inline operator bool () const noexcept {
299 return context_ !=
nullptr;
305 template<GARLIC_VIEW Layer>
311 template<GARLIC_VIEW Layer>
312 inline bool quick_test(
const Layer& value)
const noexcept;
314 inline const constraint_context& context() const noexcept {
318 inline constraint_context& context() noexcept {
322 template<
typename Tag>
323 inline const auto& context_for() const noexcept {
324 return *
reinterpret_cast<typename Tag::context_type*
>(context_.get());
327 template<
typename Tag>
328 inline auto& context_for() noexcept {
329 return *
reinterpret_cast<typename Tag::context_type*
>(context_.get());
332 template<
typename,
typename... Args>
342 template<GARLIC_VIEW Layer, typename Container, typename BackInserterIterator>
343 inline
void test_constraints(
345 Container&& constraints,
346 BackInserterIterator it) {
347 for (
const auto& constraint : constraints) {
348 if (
auto result = constraint.test(value); !result.is_valid()) {
349 it = std::move(result);
350 if (constraint.context().is_fatal())
361 template<GARLIC_VIEW Layer,
typename Container>
365 std::begin(constraints), std::end(constraints),
366 [&value](
const auto& constraint) {
return constraint.quick_test(value); }
375 template<GARLIC_VIEW Layer,
typename Container>
376 static inline ConstraintResult
378 for (
const auto& constraint : constraints) {
379 if (
auto result = constraint.test(value); !result.is_valid()) {
383 return ConstraintResult::ok();
393 struct Context :
public constraint_context {
394 template<
typename... Args>
395 Context(TypeFlag flag,
text&& name =
"type_constraint", Args&&... args
396 ) : constraint_context(std::move(name), std::forward<Args>(args)...), flag(flag) {}
401 using context_type = Context;
403 template<GARLIC_VIEW Layer>
405 test (
const Layer& layer,
const Context& context) noexcept {
406 switch (context.flag) {
407 case TypeFlag::Null: {
408 if (layer.is_null()) {
return context.ok(); }
409 else return context.fail(
"Expected null.");
411 case TypeFlag::Boolean: {
412 if (layer.is_bool()) {
return context.ok(); }
413 else return context.fail(
"Expected boolean type.");
415 case TypeFlag::Double: {
416 if (layer.is_double()) {
return context.ok(); }
417 else return context.fail(
"Expected double type.");
419 case TypeFlag::Integer: {
420 if (layer.is_int()) {
return context.ok(); }
421 else return context.fail(
"Expected integer type.");
423 case TypeFlag::String: {
424 if (layer.is_string()) {
return context.ok(); }
425 else return context.fail(
"Expected string type.");
427 case TypeFlag::List: {
428 if (layer.is_list()) {
return context.ok(); }
429 else return context.fail(
"Expected a list.");
431 case TypeFlag::Object: {
432 if (layer.is_object()) {
return context.ok(); }
433 else return context.fail(
"Expected an object.");
435 default:
return context.fail();
439 template<GARLIC_VIEW Layer>
441 quick_test(
const Layer& layer,
const Context& context) noexcept {
442 switch (context.flag) {
443 case TypeFlag::Null:
return layer.is_null();
444 case TypeFlag::Boolean:
return layer.is_bool();
445 case TypeFlag::Double:
return layer.is_double();
446 case TypeFlag::Integer:
return layer.is_int();
447 case TypeFlag::String:
return layer.is_string();
448 case TypeFlag::List:
return layer.is_list();
449 case TypeFlag::Object:
return layer.is_object();
450 default:
return false;
470 using size_type = size_t;
472 struct Context :
public constraint_context {
473 template<
typename... Args>
474 Context(size_type min, size_type max,
text&& name =
"range_constraint", Args&&... args
475 ) : constraint_context(std::move(name), std::forward<Args>(args)...), min(min), max(max) {}
481 using context_type = Context;
483 template<GARLIC_VIEW Layer>
485 test(
const Layer& layer,
const Context& context) noexcept {
486 if (layer.is_string()) {
487 auto length = layer.get_string_view().size();
488 if (length > context.max || length < context.min)
return context.fail(
"invalid string length.");
489 }
else if (layer.is_double()) {
490 auto dvalue = layer.get_double();
491 if(dvalue > context.max || dvalue < context.min)
return context.fail(
"out of range value.");
492 }
else if (layer.is_int()) {
493 auto ivalue = layer.get_int();
494 if(
static_cast<size_type
>(ivalue) > context.max ||
static_cast<size_type
>(ivalue) < context.min)
495 return context.fail(
"out of range value.");
496 }
else if (layer.is_list()) {
498 if (count > context.max)
499 return context.fail(
"too many items in the list.");
500 if (count < context.min)
501 return context.fail(
"too few items in the list.");
506 template<GARLIC_VIEW Layer>
508 quick_test(
const Layer& layer,
const Context& context) noexcept {
509 if (layer.is_string()) {
510 auto length = layer.get_string_view().size();
511 if (length > context.max || length < context.min)
return false;
512 }
else if (layer.is_double()) {
513 auto dvalue = layer.get_double();
514 if(dvalue > context.max || dvalue < context.min)
return false;
515 }
else if (layer.is_int()) {
516 auto ivalue = layer.get_int();
517 if(
static_cast<size_type
>(ivalue) > context.max ||
static_cast<size_type
>(ivalue) < context.min)
519 }
else if (layer.is_list()) {
521 if (count > context.max)
523 if (count < context.min)
541 struct Context :
public constraint_context {
542 template<
typename... Args>
543 Context(
text&& pattern,
text&& name =
"regex_constraint", Args&&... args
544 ) : constraint_context(std::move(name), std::forward<Args>(args)...),
545 pattern(pattern.data(), pattern.size()) {}
550 using context_type = Context;
552 template<GARLIC_VIEW Layer>
554 test(
const Layer& layer,
const Context& context) noexcept {
555 if (!layer.is_string())
return context.ok();
556 if (std::regex_match(layer.get_cstr(), context.pattern)) {
return context.ok(); }
557 else {
return context.fail(
"invalid value."); }
560 template<GARLIC_VIEW Layer>
562 quick_test(
const Layer& layer,
const Context& context) noexcept {
563 if (!layer.is_string())
return true;
564 if (std::regex_match(layer.get_cstr(), context.pattern))
return true;
576 struct Context :
public constraint_context {
577 template<
typename... Args>
579 ) : constraint_context(std::forward<Args>(args)...), constraints(std::move(constraints)) {}
584 using context_type = Context;
586 template<GARLIC_VIEW Layer>
588 test(
const Layer& layer,
const Context& context) noexcept {
589 if (any_tag::quick_test(layer, context))
591 return context.fail(
"None of the constraints read this value.");
594 template<GARLIC_VIEW Layer>
596 quick_test(
const Layer& layer,
const Context& context) noexcept {
598 context.constraints.begin(),
599 context.constraints.end(),
600 [&layer](
const auto& item) { return item.quick_test(layer); });
614 struct Context :
public constraint_context {
615 template<
typename... Args>
617 Constraint&& constraint,
bool ignore_details =
false,
618 text&& name =
"list_constraint", Args&&... args
619 ) : constraint_context(std::move(name), std::forward<Args>(args)...),
620 constraint(std::move(constraint)), ignore_details(ignore_details) {}
626 using context_type = Context;
628 template<GARLIC_VIEW Layer>
630 test(
const Layer& layer,
const Context& context) noexcept {
631 if (!layer.is_list())
return context.fail(
"Expected a list.");
632 if (context.ignore_details)
633 return list_tag::test<true>(layer, context);
634 return list_tag::test<false>(layer, context);
637 template<GARLIC_VIEW Layer>
639 quick_test(
const Layer& layer,
const Context& context) noexcept {
640 if (!layer.is_list())
return false;
642 layer.begin_list(), layer.end_list(),
643 [&context](
const auto& item) { return context.constraint.quick_test(item); }
647 template<
bool IgnoreDetails, GARLIC_VIEW Layer>
648 inline static ConstraintResult test(
const Layer& layer,
const Context& context) noexcept {
650 for (
const auto& item : layer.get_list()) {
652 if (!context.constraint.quick_test(item)) {
654 "Invalid value found in the list.",
655 ConstraintResult::leaf_field_failure(
656 text::copy(std::to_string(index)),
660 auto result = context.constraint.test(item);
661 if (!result.is_valid()) {
663 "Invalid value found in the list.",
664 ConstraintResult::field_failure(
665 text::copy(std::to_string(index)),
697 struct Context :
public constraint_context {
698 template<
typename... Args>
701 text&& name =
"tuple_constraint", Args&&... args
702 ) : constraint_context(std::move(name), std::forward<Args>(args)...),
703 constraints(std::move(constraints)), strict(strict), ignore_details(ignore_details) {}
710 using context_type = Context;
712 template<GARLIC_VIEW Layer>
714 quick_test(
const Layer& layer,
const Context& context) noexcept {
715 if (!layer.is_list())
return false;
716 auto tuple_it = layer.begin_list();
717 auto constraint_it = context.constraints.begin();
718 while (constraint_it != context.constraints.end() && tuple_it != layer.end_list()) {
719 if (!constraint_it->quick_test(*tuple_it))
return false;
720 std::advance(tuple_it, 1);
721 std::advance(constraint_it, 1);
723 if (context.strict && tuple_it != layer.end_list())
return false;
724 if (constraint_it != context.constraints.end())
return false;
728 template<GARLIC_VIEW Layer>
730 test(
const Layer& layer,
const Context& context) noexcept {
731 if (context.ignore_details)
732 return tuple_tag::test<true>(layer, context);
733 return tuple_tag::test<false>(layer, context);
737 template<
bool IgnoreDetails, GARLIC_VIEW Layer>
739 test(
const Layer& layer,
const Context& context) noexcept {
740 if (!layer.is_list())
741 return context.fail(
"Expected a list (tuple).");
743 auto tuple_it = layer.begin_list();
744 auto constraint_it = context.constraints.begin();
745 while (constraint_it != context.constraints.end() && tuple_it != layer.end_list()) {
747 auto result = constraint_it->quick_test(*tuple_it);
750 "Invalid value found in the tuple.",
751 ConstraintResult::leaf_field_failure(
752 text::copy(std::to_string(index)),
757 auto result = constraint_it->test(*tuple_it);
758 if (!result.is_valid()) {
760 "Invalid value found in the tuple.",
761 ConstraintResult::field_failure(
762 text::copy(std::to_string(index)),
768 std::advance(tuple_it, 1);
769 std::advance(constraint_it, 1);
772 if (context.strict && tuple_it != layer.end_list()) {
773 return context.fail(
"Too many values in the tuple.");
775 if (constraint_it != context.constraints.end()) {
776 return context.fail(
"Too few values in the tuple.");
799 struct Context :
public constraint_context {
800 template<
typename... Args>
803 text&& name =
"map_constraint", Args&&... args
804 ) : constraint_context(std::move(name), std::forward<Args>(args)...),
805 key(std::move(key_constraint)), value(std::move(value_constraint)), ignore_details(ignore_details) {}
812 using context_type = Context;
814 template<GARLIC_VIEW Layer>
815 static ConstraintResult test(
const Layer& layer,
const Context& context) noexcept {
816 if (!layer.is_object())
817 return context.fail(
"Expected an object.");
818 if (context.ignore_details)
819 return map_tag::test<true>(layer, context);
820 return map_tag::test<false>(layer, context);
823 template<GARLIC_VIEW Layer>
824 static bool quick_test(
const Layer& layer,
const Context& context) noexcept {
825 if (!layer.is_object())
829 return map_tag::quick_test<true, true>(layer, context);
830 return map_tag::quick_test<true, false>(layer, context);
831 }
else if (context.value)
832 return map_tag::quick_test<false, true>(layer, context);
837 template<
bool HasKeyConstra
int,
bool HasValueConstra
int, GARLIC_VIEW Layer>
838 static inline bool quick_test(
const Layer& layer,
const Context& context) noexcept {
840 layer.begin_member(), layer.end_member(),
841 [&context](
const auto& item) {
842 if (HasKeyConstraint && !context.key.quick_test(item.key)) return false;
843 if (HasValueConstraint && !context.value.quick_test(item.value)) return false;
848 template<
bool IgnoreDetails, GARLIC_VIEW Layer>
850 test(
const Layer& layer,
const Context& context) noexcept {
852 if (context.value)
return test<IgnoreDetails, true, true>(layer, context);
853 return test<IgnoreDetails, true, false>(layer, context);
854 }
else if (context.value)
return test<IgnoreDetails, false, true>(layer, context);
858 template<
bool IgnoreDetails,
bool HasKeyConstra
int,
bool HasValueConstra
int, GARLIC_VIEW Layer>
860 test(
const Layer& layer,
const Context& context) noexcept {
861 for (
const auto& item : layer.get_object()) {
862 if (HasKeyConstraint) {
863 if (IgnoreDetails && !context.key.quick_test(item.key)) {
864 return context.fail(
"Object contains invalid key.");
866 if (
auto result = context.key.test(item.key); !result.is_valid()) {
867 return context.fail(
"Object contains invalid key.", std::move(result));
871 if (HasValueConstraint) {
872 if (IgnoreDetails && !context.value.quick_test(item.value)) {
873 return context.fail(
"Object contains invalid value.");
875 if (
auto result = context.value.test(item.value); !result.is_valid()) {
876 return context.fail(
"Object contains invalid value.", std::move(result));
900 struct Context :
public constraint_context {
901 template<
typename... Args>
905 ) : constraint_context(std::forward<Args>(args)...),
906 constraints(std::move(constraints)), hide(hide), ignore_details(ignore_details) {}
913 using context_type = Context;
915 template<GARLIC_VIEW Layer>
917 test(
const Layer& layer,
const Context& context) noexcept {
920 if (context.ignore_details) {
924 return context.fail(
"Some of the constraints fail on this value.");
930 return context.fail(
"Some of the constraints fail on this value.", std::move(results));
933 template<GARLIC_VIEW Layer>
934 static bool quick_test(
const Layer& layer,
const Context& context) noexcept {
951 struct Context :
public constraint_context {
953 template<
typename... Args>
954 Context(T value, Args&&... args) : constraint_context(std::forward<Args>(args)...), value(value) {}
959 using context_type = Context;
961 template<GARLIC_VIEW Layer>
963 test(
const Layer& layer,
const Context& context) noexcept {
964 if (literal_tag::validate(layer, context.value))
967 return context.fail(
"invalid value.");
970 template<GARLIC_VIEW Layer>
971 static bool quick_test(
const Layer& layer,
const Context& context) noexcept {
972 return literal_tag::validate(layer, context.value);
977 using enable_if_int =
typename std::enable_if<std::is_integral<V>::value && !std::is_same<V, bool>::value,
bool>::type;
980 using enable_if_bool =
typename std::enable_if<std::is_same<V, bool>::value,
bool>::type;
983 using enable_if_double =
typename std::enable_if<std::is_floating_point<V>::value,
bool>::type;
986 using enable_if_string =
typename std::enable_if<std::is_same<V, std::string>::value,
bool>::type;
989 using enable_if_char_ptr =
typename std::enable_if<std::is_same<V, const char*>::value,
bool>::type;
991 template<
typename ValueType, GARLIC_VIEW Layer>
992 static inline enable_if_int<ValueType>
993 validate(
const Layer& layer, ValueType expectation) noexcept {
994 return layer.is_int() && expectation == layer.get_int();
997 template<
typename ValueType, GARLIC_VIEW Layer>
998 static inline enable_if_bool<ValueType>
999 validate(
const Layer& layer, ValueType expectation) noexcept {
1000 return layer.is_bool() && expectation == layer.get_bool();
1003 template<
typename ValueType, GARLIC_VIEW Layer>
1004 static inline enable_if_double<ValueType>
1005 validate(
const Layer& layer, ValueType expectation) noexcept {
1006 return layer.is_double() && expectation == layer.get_double();
1009 template<
typename ValueType, GARLIC_VIEW Layer>
1010 static inline enable_if_string<ValueType>
1011 validate(
const Layer& layer,
const ValueType& expectation) noexcept {
1012 return layer.is_string() && !expectation.compare(layer.get_cstr());
1015 template<
typename ValueType, GARLIC_VIEW Layer>
1016 static inline enable_if_char_ptr<ValueType>
1017 validate(
const Layer& layer, ValueType expectation) noexcept {
1018 return layer.is_string() && !strcmp(expectation, layer.get_cstr());
1024 using context_type = constraint_context;
1026 template<GARLIC_VIEW Layer>
1028 test(
const Layer& layer,
const context_type& context) noexcept {
1029 if (layer.is_null())
1030 return context.ok();
1031 return context.fail(
"invalid value.");
1034 template<GARLIC_VIEW Layer>
1035 static bool quick_test(
const Layer& layer,
const context_type& context) noexcept {
1036 return layer.is_null();
1040 using string_literal_tag = literal_tag<std::string>;
1041 using int_literal_tag = literal_tag<int>;
1042 using double_literal_tag = literal_tag<double>;
1043 using bool_literal_tag = literal_tag<bool>;
1044 using null_literal_tag = literal_tag<VoidType>;
1062 inline operator bool()
const {
return is_valid(); }
1066 std::unordered_map<text, text> annotations;
1069 bool ignore_details =
false;
1072 Field(Properties&& properties) : properties_(std::move(properties)) {}
1079 properties_.name = std::move(name);
1080 properties_.constraints = std::move(constraints);
1086 template<
typename Tag,
typename... Args>
1088 this->add_constraint(make_constraint<Tag>(std::forward<Args>(args)...));
1093 properties_.constraints.push_back(std::move(constraint));
1098 properties_.constraints.push_front(
1107 const auto&
annotations() const noexcept {
return properties_.annotations; }
1119 const auto& annotations = properties_.annotations;
1120 if (
auto it = annotations.find(
"message"); it != annotations.end()) {
1123 return text::no_text();
1126 const text&
name() const noexcept {
return properties_.name; }
1127 const Properties& properties() const noexcept {
return properties_; }
1130 const_constraint_iterator
begin_constraints() const noexcept {
return properties_.constraints.begin(); }
1133 const_constraint_iterator
end_constraints() const noexcept {
return properties_.constraints.end(); }
1136 template<GARLIC_VIEW Layer>
1144 template<GARLIC_VIEW Layer>
1150 Properties properties_;
1160 using field_pointer = std::shared_ptr<Field>;
1168 using const_field_iterator =
typename std::unordered_map<text, FieldDescriptor>::const_iterator;
1171 std::unordered_map<text, FieldDescriptor> field_map;
1172 std::unordered_map<text, text> annotations;
1174 bool strict =
false;
1180 Model(Properties&& properties) : properties_(std::move(properties)) {}
1184 properties_.name = std::move(name);
1194 properties_.field_map.emplace(
1201 template<
typename KeyType>
1203 auto it = properties_.field_map.find(name);
1204 if (it != properties_.field_map.end()) {
1205 return it->second.field;
1214 const auto&
annotations() const noexcept {
return properties_.annotations; }
1217 const text&
name() const noexcept {
return properties_.name; }
1218 const Properties& properties()
const {
return properties_; }
1221 const_field_iterator
begin_fields() const noexcept {
return properties_.field_map.begin(); }
1224 const_field_iterator
end_fields() const noexcept {
return properties_.field_map.end(); }
1227 const_field_iterator
find_field(
const text& name)
const noexcept {
return properties_.field_map.find(name); }
1230 template<GARLIC_VIEW Layer>
1232 if (!layer.is_object())
return false;
1233 std::unordered_set<text> requirements;
1234 for (
const auto& member : layer.get_object()) {
1235 auto it = properties_.field_map.find(member.key.get_cstr());
1236 if (it == properties_.field_map.end())
continue;
1237 if (!it->second.field->quick_test(member.value)) {
1240 requirements.emplace(it->first);
1242 for (
const auto& item : properties_.field_map) {
1243 if (
auto it = requirements.find(item.first); it != requirements.end())
continue;
1244 if (!item.second.required)
continue;
1251 template<GARLIC_VIEW Layer>
1254 if (layer.is_object()) {
1256 std::unordered_set<text> requirements;
1257 for (
const auto& member : layer.get_object()) {
1258 auto it = properties_.field_map.find(member.key.get_cstr());
1259 if (it != properties_.field_map.end()) {
1260 this->test_field(details, member.key, member.value, it->second.field);
1261 requirements.emplace(it->first);
1264 for (
const auto& item : properties_.field_map) {
1265 if (
auto it = requirements.find(item.first); it != requirements.end())
continue;
1266 if (!item.second.required)
continue;
1268 ConstraintResult::leaf_field_failure(
1270 "missing required field!"));
1273 details.push_back(ConstraintResult::leaf_failure(
"type",
"Expected object."));
1275 if (!details.
empty()) {
1277 .
details = std::move(details),
1278 .name = properties_.name.clone(),
1279 .reason =
text(
"This model is invalid!"),
1280 .flag = ConstraintResult::flags::none
1284 return ConstraintResult::ok();
1288 Properties properties_;
1291 template<GARLIC_VIEW Layer>
1297 const field_pointer& field)
const {
1298 if (field->properties().ignore_details) {
1299 if (!field->quick_test(value)) {
1302 .name = decode<text>(key),
1303 .reason = field->message(),
1304 .flag = ConstraintResult::flags::field
1308 auto test = field->validate(value);
1309 if (!test.is_valid()) {
1310 details.push_back(ConstraintResult {
1311 .details = std::move(test.failures),
1312 .name = decode<text>(key),
1313 .reason = field->message(),
1314 .flag = ConstraintResult::flags::field
1328 struct Context :
public constraint_context {
1329 using model_pointer = std::shared_ptr<Model>;
1331 template<
typename... Args>
1332 Context(model_pointer model, Args&&... args
1333 ) : constraint_context(std::forward<Args>(args)...), model(std::move(model)) {}
1335 Context(model_pointer model
1336 ) : constraint_context(model->name().view(), text::no_text(),
true), model(std::move(model)) {}
1338 model_pointer model;
1341 using context_type = Context;
1343 template<GARLIC_VIEW Layer>
1344 static inline ConstraintResult test(
const Layer& layer,
const Context& context) noexcept {
1345 return context.model->validate(layer);
1348 template<GARLIC_VIEW Layer>
1349 static inline bool quick_test(
const Layer& layer,
const Context& context) noexcept {
1350 return context.model->quick_test(layer);
1354 static inline std::shared_ptr<Field> make_field(
text&& name) {
1355 return std::make_shared<Field>(std::move(name));
1358 static inline std::shared_ptr<Field> make_field(text&& name, sequence<Constraint>&& constraints) {
1359 return std::make_shared<Field>(std::move(name), std::move(constraints));
1362 static inline std::shared_ptr<Field> make_field(sequence<Constraint>&& constraints) {
1363 return std::make_shared<Field>(text::no_text(), std::move(constraints));
1366 template<
typename... Args>
1367 static inline std::shared_ptr<Model> make_model(Args&&...args) {
1368 return std::make_shared<Model>(std::forward<Args>(args)...);
1386 struct Context :
public constraint_context {
1387 using field_pointer = std::shared_ptr<Field>;
1388 using field_pointer_ref = std::shared_ptr<field_pointer>;
1390 template<
typename... Args>
1392 field_pointer_ref ref,
bool hide =
false,
bool ignore_details =
false,
1394 ) : constraint_context(std::forward<Args>(args)...),
1395 ref(std::move(ref)), hide(hide), ignore_details(ignore_details) {
1396 this->update_name();
1399 template<
typename... Args>
1401 field_pointer pointer,
bool hide =
false,
bool ignore_details =
false,
1403 ) : Context(std::make_shared<field_pointer>(pointer), hide, ignore_details, std::forward<Args>(args)...) {}
1405 template<
typename... Args>
1407 custom_message_fail(Args&&... args)
const noexcept {
1408 return this->fail((*ref)->message(), std::forward<Args>(args)...);
1411 void update_name() {
1412 if (*ref && this->name.empty()) {
1413 this->name = (*ref)->name();
1417 void set_field(field_pointer field) {
1419 this->update_name();
1422 field_pointer_ref ref;
1424 bool ignore_details;
1427 using context_type = Context;
1429 template<GARLIC_VIEW Layer>
1431 test(
const Layer& layer,
const Context& context) noexcept {
1435 const auto& field = *context.ref;
1436 if (context.ignore_details || field->properties().ignore_details) {
1437 if (field->quick_test(layer))
return context.ok();
1438 return context.custom_message_fail();
1440 auto result = field->validate(layer);
1441 if (result.is_valid())
return context.ok();
1442 return context.custom_message_fail(std::move(result.failures));
1445 template<GARLIC_VIEW Layer>
1446 static inline bool quick_test(
const Layer& layer,
const Context& context) noexcept {
1447 return (*context.ref)->quick_test(layer);
1453 type_tag,
range_tag,
regex_tag,
any_tag,
list_tag,
tuple_tag,
map_tag,
all_tag,
model_tag,
field_tag,
1456 template<
typename Tag,
typename... Args>
1458 constexpr
auto position = constraint_registry::position_of<Tag>();
1459 static_assert(position != constraint_registry::not_found,
"Unregistered tag used to make a flat constraint.");
1462 std::make_shared<typename Tag::context_type>(std::forward<Args>(args)...));
1465 template<GARLIC_VIEW Layer>
1468 static constexpr
auto handlers = constraint_registry::test_handlers<Layer>();
1469 return handlers[index_](value, context_.get());
1472 template<GARLIC_VIEW Layer>
1475 static constexpr
auto handlers = constraint_registry::quick_test_handlers<Layer>();
1476 return handlers[index_](value, context_.get());
1484 template<
typename Tag,
typename... Args>
1487 return Constraint::make<Tag>(std::forward<Args>(args)...);