constraints.h
Go to the documentation of this file.
1 #ifndef GARLIC_CONSTRAINTS_H
2 #define GARLIC_CONSTRAINTS_H
3 
5 
6 #include <algorithm>
7 #include <unordered_set>
8 #include <regex>
9 
10 #include "layer.h"
11 #include "utility.h"
12 #include "containers.h"
13 
14 
15 namespace garlic {
16 
19 
21 
22  enum flags : uint8_t {
23  none = 0x1 << 0,
24  valid = 0x1 << 1,
25  field = 0x1 << 2,
26  };
27 
29 
31 
33 
34  flags flag;
35 
37  inline bool is_leaf() const noexcept { return !details.size(); }
38 
40  inline bool is_valid() const noexcept { return flag & flags::valid; }
41 
43  inline bool is_field() const noexcept { return flag & flags::field; }
44 
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); }
47 
49  inline operator bool() const { return is_valid(); }
50 
51  template<flags Flag = flags::none>
52  static ConstraintResult leaf_failure(
53  text&& name, text&& reason=text::no_text()) noexcept {
54  return ConstraintResult {
56  .name = std::move(name),
57  .reason = std::move(reason),
58  .flag = Flag
59  };
60  }
61 
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));
65  }
66 
67  static inline ConstraintResult
68  field_failure(
69  text&& name,
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 {
75  .details = std::move(details),
76  .name = std::move(name),
77  .reason = std::move(reason),
78  .flag = flags::field
79  };
80  }
81 
82  static ConstraintResult ok() noexcept {
83  return ConstraintResult {
84  .details = sequence<ConstraintResult>::no_sequence(),
85  .name = text::no_text(),
86  .reason = text::no_text(),
87  .flag = flags::valid
88  };
89  }
90 
91  };
92 
93  class constraint_context {
94  public:
95  enum flags : uint8_t {
96  none = 0x1 << 0,
97  fatal = 0x1 << 1, // should stop looking at other constraints.
98  };
99 
100  text name; // constraint name.
101  text message; // custom rejection reason.
102  flags flag = flags::none;
103 
104  constraint_context(
105  text&& name = text::no_text(),
106  text&& message = text::no_text(),
107  bool fatal = false
108  ) : name(std::move(name)), message(std::move(message)), flag(fatal ? flags::fatal : flags::none) {}
109 
110  virtual ~constraint_context() = default;
111 
112  inline bool is_fatal() const noexcept { return flag & flags::fatal; }
113 
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>()),
118  .name = name,
119  .reason = message,
120  .flag = (Field ? ConstraintResult::flags::field : ConstraintResult::flags::none)
121  };
122  }
123 
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>()),
129  .name = name,
130  .reason = message,
131  .flag = (Field ? ConstraintResult::flags::field : ConstraintResult::flags::none)
132  };
133  return this->fail<Field, Leaf>();
134  }
135 
136  template<bool Field = false>
137  auto fail(const text& message, sequence<ConstraintResult>&& details) const noexcept {
138  return ConstraintResult {
139  .details = std::move(details),
140  .name = name,
141  .reason = (this->message.empty() ? message : this->message),
142  .flag = (Field ? ConstraintResult::flags::field : ConstraintResult::flags::none)
143  };
144  }
145 
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));
151  }
152 
153  inline auto ok() const noexcept -> ConstraintResult {
154  return ConstraintResult::ok();
155  }
156  };
157 
158  template<GARLIC_VIEW Layer>
159  static inline text
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());
163  });
164  return default_value;
165  }
166 
167  template<typename T>
168  using constraint_test_handler = ConstraintResult (*)(const T&, const constraint_context*);
169 
170  template<typename T>
171  using constraint_quick_test_handler = bool (*)(const T&, const constraint_context*);
172 
173  namespace internal {
174  template<typename InnerTag>
175  struct tag_wrapper {
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));
180  }
181 
182  template<GARLIC_VIEW Layer>
183  static inline bool
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));
186  }
187  };
188 
189  template<typename Tag, typename... Rest>
190  struct registry {
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;
195 
196  template<typename Query>
197  static unsigned constexpr position_of() {
198  if (std::is_same<Tag, Query>::value)
199  return 0;
200  constexpr auto result = rest::template position_of<Query>();
201  if (result == not_found)
202  return result;
203  return result + 1;
204  }
205 
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>();
211  }
212 
213  template<GARLIC_VIEW Layer>
214  static constexpr std::array<constraint_test_handler<Layer>, sizeof...(Rest) + 1>
215  test_handlers() {
216  return {&tag::template test<Layer>, &tag_wrapper<Rest>::template test<Layer>...};
217  }
218 
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>...};
223  }
224  };
225 
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;
231 
232  template<typename Query>
233  static unsigned constexpr position_of() {
234  return (std::is_same<Tag, Query>::value ? 0 : not_found);
235  }
236 
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);
240  }
241 
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);
245  }
246  };
247 
248  }
249 
251 
275  class Constraint final {
276  private:
277  using context_pointer = std::shared_ptr<constraint_context>;
278  context_pointer context_;
279  unsigned index_;
280 
281  Constraint(unsigned index, context_pointer&& context) : context_(std::move(context)), index_(index) {}
282 
283  public:
285 
289  template<typename Tag, typename... Args>
290  static Constraint make(Args&&... args) noexcept;
291 
293  static Constraint empty() noexcept {
294  return Constraint(0, nullptr);
295  }
296 
298  inline operator bool () const noexcept {
299  return context_ != nullptr;
300  }
301 
303 
305  template<GARLIC_VIEW Layer>
306  inline ConstraintResult test(const Layer& value) const noexcept;
307 
309 
311  template<GARLIC_VIEW Layer>
312  inline bool quick_test(const Layer& value) const noexcept;
313 
314  inline const constraint_context& context() const noexcept {
315  return *context_;
316  }
317 
318  inline constraint_context& context() noexcept {
319  return *context_;
320  }
321 
322  template<typename Tag>
323  inline const auto& context_for() const noexcept {
324  return *reinterpret_cast<typename Tag::context_type*>(context_.get());
325  }
326 
327  template<typename Tag>
328  inline auto& context_for() noexcept {
329  return *reinterpret_cast<typename Tag::context_type*>(context_.get());
330  }
331 
332  template<typename, typename... Args>
333  friend inline Constraint make_constraint(Args&&...) noexcept;
334  };
335 
336 
338 
342  template<GARLIC_VIEW Layer, typename Container, typename BackInserterIterator>
343  inline void test_constraints(
344  Layer&& value,
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())
351  break;
352  }
353  }
354  }
355 
356 
361  template<GARLIC_VIEW Layer, typename Container>
362  static inline bool
363  test_constraints_quick(Layer&& value, Container&& constraints) {
364  return std::all_of(
365  std::begin(constraints), std::end(constraints),
366  [&value](const auto& constraint) { return constraint.quick_test(value); }
367  );
368  }
369 
370 
375  template<GARLIC_VIEW Layer, typename Container>
376  static inline ConstraintResult
377  test_constraints_first_failure(Layer&& value, Container&& constraints) {
378  for (const auto& constraint : constraints) {
379  if (auto result = constraint.test(value); !result.is_valid()) {
380  return result;
381  }
382  }
383  return ConstraintResult::ok();
384  }
385 
387 
391  struct type_tag {
392 
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) {}
397 
398  TypeFlag flag;
399  };
400 
401  using context_type = Context;
402 
403  template<GARLIC_VIEW Layer>
404  static ConstraintResult
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.");
410  }
411  case TypeFlag::Boolean: {
412  if (layer.is_bool()) { return context.ok(); }
413  else return context.fail("Expected boolean type.");
414  }
415  case TypeFlag::Double: {
416  if (layer.is_double()) { return context.ok(); }
417  else return context.fail("Expected double type.");
418  }
419  case TypeFlag::Integer: {
420  if (layer.is_int()) { return context.ok(); }
421  else return context.fail("Expected integer type.");
422  }
423  case TypeFlag::String: {
424  if (layer.is_string()) { return context.ok(); }
425  else return context.fail("Expected string type.");
426  }
427  case TypeFlag::List: {
428  if (layer.is_list()) { return context.ok(); }
429  else return context.fail("Expected a list.");
430  }
431  case TypeFlag::Object: {
432  if (layer.is_object()) { return context.ok(); }
433  else return context.fail("Expected an object.");
434  }
435  default: return context.fail();
436  }
437  }
438 
439  template<GARLIC_VIEW Layer>
440  static bool
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;
451  }
452 
453  }
454  };
455 
456 
458 
469  struct range_tag {
470  using size_type = size_t;
471 
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) {}
476 
477  size_type min;
478  size_type max;
479  };
480 
481  using context_type = Context;
482 
483  template<GARLIC_VIEW Layer>
484  static ConstraintResult
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()) {
497  size_type count = garlic::list_size(layer);
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.");
502  }
503  return context.ok();
504  }
505 
506  template<GARLIC_VIEW Layer>
507  static bool
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)
518  return false;
519  } else if (layer.is_list()) {
520  size_type count = garlic::list_size(layer);
521  if (count > context.max)
522  return false;
523  if (count < context.min)
524  return false;
525  }
526  return true;
527 
528  }
529  };
530 
531 
533 
539  struct regex_tag {
540 
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()) {}
546 
547  std::regex pattern;
548  };
549 
550  using context_type = Context;
551 
552  template<GARLIC_VIEW Layer>
553  static inline ConstraintResult
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."); }
558  }
559 
560  template<GARLIC_VIEW Layer>
561  static inline bool
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;
565  else return false;
566  }
567  };
568 
569 
571 
575  struct any_tag {
576  struct Context : public constraint_context {
577  template<typename... Args>
578  Context(sequence<Constraint>&& constraints, Args&&... args
579  ) : constraint_context(std::forward<Args>(args)...), constraints(std::move(constraints)) {}
580 
581  sequence<Constraint> constraints;
582  };
583 
584  using context_type = Context;
585 
586  template<GARLIC_VIEW Layer>
587  static inline ConstraintResult
588  test(const Layer& layer, const Context& context) noexcept {
589  if (any_tag::quick_test(layer, context))
590  return context.ok();
591  return context.fail("None of the constraints read this value.");
592  }
593 
594  template<GARLIC_VIEW Layer>
595  static inline bool
596  quick_test(const Layer& layer, const Context& context) noexcept {
597  return std::any_of(
598  context.constraints.begin(),
599  context.constraints.end(),
600  [&layer](const auto& item) { return item.quick_test(layer); });
601  }
602  };
603 
604 
613  struct list_tag {
614  struct Context : public constraint_context {
615  template<typename... Args>
616  Context(
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) {}
621 
622  Constraint constraint;
623  bool ignore_details;
624  };
625 
626  using context_type = Context;
627 
628  template<GARLIC_VIEW Layer>
629  static ConstraintResult
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);
635  }
636 
637  template<GARLIC_VIEW Layer>
638  static bool
639  quick_test(const Layer& layer, const Context& context) noexcept {
640  if (!layer.is_list()) return false;
641  return std::all_of(
642  layer.begin_list(), layer.end_list(),
643  [&context](const auto& item) { return context.constraint.quick_test(item); }
644  );
645  }
646 
647  template<bool IgnoreDetails, GARLIC_VIEW Layer>
648  inline static ConstraintResult test(const Layer& layer, const Context& context) noexcept {
649  size_t index = 0;
650  for (const auto& item : layer.get_list()) {
651  if (IgnoreDetails) { // no runtime penalty since IgnoreDetails is a template parameter.
652  if (!context.constraint.quick_test(item)) {
653  return context.fail(
654  "Invalid value found in the list.",
655  ConstraintResult::leaf_field_failure(
656  text::copy(std::to_string(index)),
657  "invalid value."));
658  }
659  } else {
660  auto result = context.constraint.test(item);
661  if (!result.is_valid()) {
662  return context.fail(
663  "Invalid value found in the list.",
664  ConstraintResult::field_failure(
665  text::copy(std::to_string(index)),
666  std::move(result),
667  "invalid value."
668  ));
669  }
670  }
671  ++index;
672  }
673  return context.ok();
674  }
675  };
676 
695  class tuple_tag {
696  public:
697  struct Context : public constraint_context {
698  template<typename... Args>
699  Context(
700  sequence<Constraint>&& constraints, bool strict = true, bool ignore_details = false,
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) {}
704 
705  sequence<Constraint> constraints;
706  bool strict;
707  bool ignore_details;
708  };
709 
710  using context_type = Context;
711 
712  template<GARLIC_VIEW Layer>
713  static bool
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);
722  }
723  if (context.strict && tuple_it != layer.end_list()) return false;
724  if (constraint_it != context.constraints.end()) return false;
725  return true;
726  }
727 
728  template<GARLIC_VIEW Layer>
729  static ConstraintResult
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);
734  }
735 
736  private:
737  template<bool IgnoreDetails, GARLIC_VIEW Layer>
738  static inline ConstraintResult
739  test(const Layer& layer, const Context& context) noexcept {
740  if (!layer.is_list())
741  return context.fail("Expected a list (tuple).");
742  size_t index = 0;
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()) {
746  if (IgnoreDetails) {
747  auto result = constraint_it->quick_test(*tuple_it);
748  if (!result) {
749  return context.fail(
750  "Invalid value found in the tuple.",
751  ConstraintResult::leaf_field_failure(
752  text::copy(std::to_string(index)),
753  "invalid value."
754  ));
755  }
756  } else {
757  auto result = constraint_it->test(*tuple_it);
758  if (!result.is_valid()) {
759  return context.fail(
760  "Invalid value found in the tuple.",
761  ConstraintResult::field_failure(
762  text::copy(std::to_string(index)),
763  std::move(result),
764  "invalid value."
765  ));
766  }
767  }
768  std::advance(tuple_it, 1);
769  std::advance(constraint_it, 1);
770  ++index;
771  }
772  if (context.strict && tuple_it != layer.end_list()) {
773  return context.fail("Too many values in the tuple.");
774  }
775  if (constraint_it != context.constraints.end()) {
776  return context.fail("Too few values in the tuple.");
777  }
778  return context.ok();
779  }
780  };
781 
797  class map_tag {
798  public:
799  struct Context : public constraint_context {
800  template<typename... Args>
801  Context(
802  Constraint&& key_constraint, Constraint&& value_constraint, bool ignore_details = false,
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) {}
806 
807  Constraint key;
808  Constraint value;
809  bool ignore_details;
810  };
811 
812  using context_type = Context;
813 
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);
821  }
822 
823  template<GARLIC_VIEW Layer>
824  static bool quick_test(const Layer& layer, const Context& context) noexcept {
825  if (!layer.is_object())
826  return false;
827  if (context.key) {
828  if (context.value)
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);
833  return true; // meaning there is not key or value constraint.
834  }
835 
836  private:
837  template<bool HasKeyConstraint, bool HasValueConstraint, GARLIC_VIEW Layer>
838  static inline bool quick_test(const Layer& layer, const Context& context) noexcept {
839  return std::all_of(
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;
844  return true;
845  });
846  }
847 
848  template<bool IgnoreDetails, GARLIC_VIEW Layer>
849  static inline ConstraintResult
850  test(const Layer& layer, const Context& context) noexcept {
851  if (context.key) {
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);
855  return context.ok(); // meaning there is no key or value constraint.
856  }
857 
858  template<bool IgnoreDetails, bool HasKeyConstraint, bool HasValueConstraint, GARLIC_VIEW Layer>
859  static inline ConstraintResult
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.");
865  } else {
866  if (auto result = context.key.test(item.key); !result.is_valid()) {
867  return context.fail("Object contains invalid key.", std::move(result));
868  }
869  }
870  }
871  if (HasValueConstraint) {
872  if (IgnoreDetails && !context.value.quick_test(item.value)) {
873  return context.fail("Object contains invalid value.");
874  } else {
875  if (auto result = context.value.test(item.value); !result.is_valid()) {
876  return context.fail("Object contains invalid value.", std::move(result));
877  }
878  }
879  }
880  }
881  return context.ok();
882  }
883  };
884 
899  struct all_tag {
900  struct Context : public constraint_context {
901  template<typename... Args>
902  Context(
903  sequence<Constraint>&& constraints, bool hide = true, bool ignore_details = false,
904  Args&&... args
905  ) : constraint_context(std::forward<Args>(args)...),
906  constraints(std::move(constraints)), hide(hide), ignore_details(ignore_details) {}
907 
908  sequence<Constraint> constraints;
909  bool hide;
910  bool ignore_details;
911  };
912 
913  using context_type = Context;
914 
915  template<GARLIC_VIEW Layer>
916  static ConstraintResult
917  test(const Layer& layer, const Context& context) noexcept {
918  if (context.hide)
919  return test_constraints_first_failure(layer, context.constraints);
920  if (context.ignore_details) {
921  if (test_constraints_quick(layer, context.constraints))
922  return context.ok();
923  else
924  return context.fail("Some of the constraints fail on this value.");
925  }
927  test_constraints(layer, context.constraints, std::back_inserter(results));
928  if (results.empty())
929  return context.ok();
930  return context.fail("Some of the constraints fail on this value.", std::move(results));
931  }
932 
933  template<GARLIC_VIEW Layer>
934  static bool quick_test(const Layer& layer, const Context& context) noexcept {
935  return test_constraints_quick(layer, context.constraints);
936  }
937  };
938 
949  template<typename T>
950  struct literal_tag {
951  struct Context : public constraint_context {
952 
953  template<typename... Args>
954  Context(T value, Args&&... args) : constraint_context(std::forward<Args>(args)...), value(value) {}
955 
956  T value;
957  };
958 
959  using context_type = Context;
960 
961  template<GARLIC_VIEW Layer>
962  static ConstraintResult
963  test(const Layer& layer, const Context& context) noexcept {
964  if (literal_tag::validate(layer, context.value))
965  return context.ok();
966  else
967  return context.fail("invalid value.");
968  }
969 
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);
973  }
974 
975  private:
976  template<typename V>
977  using enable_if_int = typename std::enable_if<std::is_integral<V>::value && !std::is_same<V, bool>::value, bool>::type;
978 
979  template<typename V>
980  using enable_if_bool = typename std::enable_if<std::is_same<V, bool>::value, bool>::type;
981 
982  template<typename V>
983  using enable_if_double = typename std::enable_if<std::is_floating_point<V>::value, bool>::type;
984 
985  template<typename V>
986  using enable_if_string = typename std::enable_if<std::is_same<V, std::string>::value, bool>::type;
987 
988  template<typename V>
989  using enable_if_char_ptr = typename std::enable_if<std::is_same<V, const char*>::value, bool>::type;
990 
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();
995  }
996 
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();
1001  }
1002 
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();
1007  }
1008 
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());
1013  }
1014 
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());
1019  }
1020  };
1021 
1022  template<>
1023  struct literal_tag<VoidType> {
1024  using context_type = constraint_context;
1025 
1026  template<GARLIC_VIEW Layer>
1027  static ConstraintResult
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.");
1032  }
1033 
1034  template<GARLIC_VIEW Layer>
1035  static bool quick_test(const Layer& layer, const context_type& context) noexcept {
1036  return layer.is_null();
1037  }
1038  };
1039 
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>;
1045 
1047  class Field {
1048  public:
1049 
1050  using const_constraint_iterator = typename sequence<Constraint>::const_iterator;
1051 
1055 
1057 
1059  inline bool is_valid() const noexcept { return failures.empty(); }
1060 
1062  inline operator bool() const { return is_valid(); }
1063  };
1064 
1065  struct Properties {
1066  std::unordered_map<text, text> annotations;
1067  sequence<Constraint> constraints;
1068  text name;
1069  bool ignore_details = false;
1070  };
1071 
1072  Field(Properties&& properties) : properties_(std::move(properties)) {}
1073 
1075  Field(text&& name) { properties_.name = std::move(name); }
1076 
1078  Field(text&& name, sequence<Constraint>&& constraints) {
1079  properties_.name = std::move(name);
1080  properties_.constraints = std::move(constraints);
1081  }
1082 
1084 
1086  template<typename Tag, typename... Args>
1087  void add_constraint(Args&&... args) noexcept {
1088  this->add_constraint(make_constraint<Tag>(std::forward<Args>(args)...));
1089  }
1090 
1092  void add_constraint(Constraint&& constraint) {
1093  properties_.constraints.push_back(std::move(constraint));
1094  }
1095 
1097  inline void inherit_constraints_from(const Field& another) {
1098  properties_.constraints.push_front(
1099  another.begin_constraints(),
1100  another.end_constraints());
1101  }
1102 
1104  auto& annotations() noexcept { return properties_.annotations; }
1105 
1107  const auto& annotations() const noexcept { return properties_.annotations; }
1108 
1112  bool ignore_details() const { return properties_.ignore_details; }
1113 
1115  void set_ignore_details(bool value) { properties_.ignore_details = value; }
1116 
1118  text message() const noexcept {
1119  const auto& annotations = properties_.annotations;
1120  if (auto it = annotations.find("message"); it != annotations.end()) {
1121  return it->second;
1122  }
1123  return text::no_text();
1124  }
1126  const text& name() const noexcept { return properties_.name; }
1127  const Properties& properties() const noexcept { return properties_; }
1128 
1130  const_constraint_iterator begin_constraints() const noexcept { return properties_.constraints.begin(); }
1131 
1133  const_constraint_iterator end_constraints() const noexcept { return properties_.constraints.end(); }
1134 
1136  template<GARLIC_VIEW Layer>
1137  ValidationResult validate(const Layer& layer) const noexcept {
1138  ValidationResult result;
1139  test_constraints(layer, properties_.constraints, std::back_inserter(result.failures));
1140  return result;
1141  }
1142 
1144  template<GARLIC_VIEW Layer>
1145  bool quick_test(const Layer& layer) const noexcept {
1146  return test_constraints_quick(layer, properties_.constraints);
1147  }
1148 
1149  protected:
1150  Properties properties_;
1151  };
1152 
1153 
1155 
1157  class Model {
1158  public:
1159 
1160  using field_pointer = std::shared_ptr<Field>;
1161 
1164  field_pointer field;
1165  bool required;
1166  };
1167 
1168  using const_field_iterator = typename std::unordered_map<text, FieldDescriptor>::const_iterator;
1169 
1170  struct Properties {
1171  std::unordered_map<text, FieldDescriptor> field_map;
1172  std::unordered_map<text, text> annotations;
1173  text name;
1174  bool strict = false;
1175  };
1176 
1178  Model() {}
1179 
1180  Model(Properties&& properties) : properties_(std::move(properties)) {}
1181 
1183  Model(text&& name) {
1184  properties_.name = std::move(name);
1185  }
1186 
1188 
1193  void add_field(text&& name, field_pointer field, bool required = true) {
1194  properties_.field_map.emplace(
1195  std::move(name),
1196  FieldDescriptor { .field = std::move(field), .required = required }
1197  );
1198  }
1199 
1201  template<typename KeyType>
1202  field_pointer get_field(KeyType&& name) const {
1203  auto it = properties_.field_map.find(name);
1204  if (it != properties_.field_map.end()) {
1205  return it->second.field;
1206  }
1207  return nullptr;
1208  }
1209 
1211  auto& annotations() noexcept { return properties_.annotations; }
1212 
1214  const auto& annotations() const noexcept { return properties_.annotations; }
1215 
1217  const text& name() const noexcept { return properties_.name; }
1218  const Properties& properties() const { return properties_; }
1219 
1221  const_field_iterator begin_fields() const noexcept { return properties_.field_map.begin(); }
1222 
1224  const_field_iterator end_fields() const noexcept { return properties_.field_map.end(); }
1225 
1227  const_field_iterator find_field(const text& name) const noexcept { return properties_.field_map.find(name); }
1228 
1230  template<GARLIC_VIEW Layer>
1231  bool quick_test(const Layer& layer) const noexcept {
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)) {
1238  return false;
1239  }
1240  requirements.emplace(it->first);
1241  }
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;
1245  return false;
1246  }
1247  return true;
1248  }
1249 
1251  template<GARLIC_VIEW Layer>
1252  ConstraintResult validate(const Layer& layer) const noexcept {
1254  if (layer.is_object()) {
1255  // todo : if the container allows for atomic table look up, swap the loop.
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);
1262  }
1263  }
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;
1267  details.push_back(
1268  ConstraintResult::leaf_field_failure(
1269  item.first.view(),
1270  "missing required field!"));
1271  }
1272  } else {
1273  details.push_back(ConstraintResult::leaf_failure("type", "Expected object."));
1274  }
1275  if (!details.empty()) {
1276  return ConstraintResult {
1277  .details = std::move(details),
1278  .name = properties_.name.clone(),
1279  .reason = text("This model is invalid!"),
1280  .flag = ConstraintResult::flags::none
1281  };
1282  }
1283 
1284  return ConstraintResult::ok();
1285  }
1286 
1287  protected:
1288  Properties properties_;
1289 
1290  private:
1291  template<GARLIC_VIEW Layer>
1292  inline void
1293  test_field(
1294  sequence<ConstraintResult>& details,
1295  const Layer& key,
1296  const Layer& value,
1297  const field_pointer& field) const {
1298  if (field->properties().ignore_details) {
1299  if (!field->quick_test(value)) {
1300  details.push_back(ConstraintResult {
1302  .name = decode<text>(key),
1303  .reason = field->message(),
1304  .flag = ConstraintResult::flags::field
1305  });
1306  }
1307  } else {
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
1315  });
1316  }
1317  }
1318  }
1319  };
1320 
1327  struct model_tag {
1328  struct Context : public constraint_context {
1329  using model_pointer = std::shared_ptr<Model>;
1330 
1331  template<typename... Args>
1332  Context(model_pointer model, Args&&... args
1333  ) : constraint_context(std::forward<Args>(args)...), model(std::move(model)) {}
1334 
1335  Context(model_pointer model
1336  ) : constraint_context(model->name().view(), text::no_text(), true), model(std::move(model)) {}
1337 
1338  model_pointer model;
1339  };
1340 
1341  using context_type = Context;
1342 
1343  template<GARLIC_VIEW Layer>
1344  static inline ConstraintResult test(const Layer& layer, const Context& context) noexcept {
1345  return context.model->validate(layer);
1346  }
1347 
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);
1351  }
1352  };
1353 
1354  static inline std::shared_ptr<Field> make_field(text&& name) {
1355  return std::make_shared<Field>(std::move(name));
1356  }
1357 
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));
1360  }
1361 
1362  static inline std::shared_ptr<Field> make_field(sequence<Constraint>&& constraints) {
1363  return std::make_shared<Field>(text::no_text(), std::move(constraints));
1364  }
1365 
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)...);
1369  }
1370 
1385  struct field_tag {
1386  struct Context : public constraint_context {
1387  using field_pointer = std::shared_ptr<Field>;
1388  using field_pointer_ref = std::shared_ptr<field_pointer>;
1389 
1390  template<typename... Args>
1391  Context(
1392  field_pointer_ref ref, bool hide = false, bool ignore_details = false,
1393  Args&&... args
1394  ) : constraint_context(std::forward<Args>(args)...),
1395  ref(std::move(ref)), hide(hide), ignore_details(ignore_details) {
1396  this->update_name();
1397  }
1398 
1399  template<typename... Args>
1400  Context(
1401  field_pointer pointer, bool hide = false, bool ignore_details = false,
1402  Args&&... args
1403  ) : Context(std::make_shared<field_pointer>(pointer), hide, ignore_details, std::forward<Args>(args)...) {}
1404 
1405  template<typename... Args>
1406  inline ConstraintResult
1407  custom_message_fail(Args&&... args) const noexcept {
1408  return this->fail((*ref)->message(), std::forward<Args>(args)...);
1409  }
1410 
1411  void update_name() {
1412  if (*ref && this->name.empty()) {
1413  this->name = (*ref)->name();
1414  }
1415  }
1416 
1417  void set_field(field_pointer field) {
1418  ref->swap(field);
1419  this->update_name();
1420  }
1421 
1422  field_pointer_ref ref;
1423  bool hide;
1424  bool ignore_details;
1425  };
1426 
1427  using context_type = Context;
1428 
1429  template<GARLIC_VIEW Layer>
1430  static inline ConstraintResult
1431  test(const Layer& layer, const Context& context) noexcept {
1432  if (context.hide) {
1433  return test_constraints_first_failure(layer, (*context.ref)->properties().constraints);
1434  }
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();
1439  }
1440  auto result = field->validate(layer);
1441  if (result.is_valid()) return context.ok();
1442  return context.custom_message_fail(std::move(result.failures));
1443  }
1444 
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);
1448  }
1449  };
1450 
1452  using constraint_registry = internal::registry<
1455 
1456  template<typename Tag, typename... Args>
1457  Constraint Constraint::make(Args&&... args) noexcept {
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.");
1460  return Constraint(
1461  position,
1462  std::make_shared<typename Tag::context_type>(std::forward<Args>(args)...));
1463  }
1464 
1465  template<GARLIC_VIEW Layer>
1466  inline ConstraintResult
1467  Constraint::test(const Layer& value) const noexcept {
1468  static constexpr auto handlers = constraint_registry::test_handlers<Layer>();
1469  return handlers[index_](value, context_.get());
1470  }
1471 
1472  template<GARLIC_VIEW Layer>
1473  inline bool
1474  Constraint::quick_test(const Layer& value) const noexcept {
1475  static constexpr auto handlers = constraint_registry::quick_test_handlers<Layer>();
1476  return handlers[index_](value, context_.get());
1477  }
1478 
1480 
1484  template<typename Tag, typename... Args>
1485  inline Constraint
1486  make_constraint(Args&&... args) noexcept {
1487  return Constraint::make<Tag>(std::forward<Args>(args)...);
1488  }
1489 
1490 }
1491 
1492 #endif /* end of include guard: GARLIC_CONSTRAINTS_H */
garlic::Constraint
Smallest unit of data validation.
Definition: constraints.h:275
garlic::Field::Field
Field(text &&name, sequence< Constraint > &&constraints)
Create a Field by a name and a defined set of constraints.
Definition: constraints.h:1078
garlic::Field::add_constraint
void add_constraint(Args &&... args) noexcept
Helper method to create and add a constriant to the Field.
Definition: constraints.h:1087
garlic::basic_text< char >
garlic::Field::ValidationResult::is_valid
bool is_valid() const noexcept
Definition: constraints.h:1059
garlic::range_tag
Constraint Tag that passes if the layer is within a certain boundary.
Definition: constraints.h:469
garlic::Model::FieldDescriptor::required
bool required
whether or not this Field is required.
Definition: constraints.h:1165
garlic::Field::inherit_constraints_from
void inherit_constraints_from(const Field &another)
Adds all the constraints from the specified field to the front.
Definition: constraints.h:1097
garlic::literal_tag
Constraint Tag that passes if layer is equal to a specified value.
Definition: constraints.h:950
garlic::Field::begin_constraints
const_constraint_iterator begin_constraints() const noexcept
Definition: constraints.h:1130
garlic::model_tag
Constraint Tag that passes if the specified Model passes the layer.
Definition: constraints.h:1327
garlic::list_tag
Constraint Tag that passes if the layer is a list and all its elements pass the inner constraints.
Definition: constraints.h:613
garlic::Constraint::make
static Constraint make(Args &&... args) noexcept
Create a generic Constraint based on a constraint tag.
Definition: constraints.h:1457
layer.h
This file contains concepts for C++ 20 along with some helper types to make it easier to produce iter...
utility.h
This file contains various functions and classes to work with layers like getting or resolving using ...
garlic::map_tag
Constraint Tag that passes if the layer's members pass key and value constraints.
Definition: constraints.h:797
garlic::Constraint::test
ConstraintResult test(const Layer &value) const noexcept
Perform the constraint test on the layer and return the result.
Definition: constraints.h:1467
garlic::Model::quick_test
bool quick_test(const Layer &layer) const noexcept
Run a quick test on a layer.
Definition: constraints.h:1231
garlic::Model::FieldDescriptor
Field Record.
Definition: constraints.h:1163
garlic::Field::Field
Field(text &&name)
Create an empty Field by a name.
Definition: constraints.h:1075
garlic::Model::Model
Model(text &&name)
Create an empty Model by name.
Definition: constraints.h:1183
garlic::Field::annotations
auto & annotations() noexcept
Definition: constraints.h:1104
garlic::Model::validate
ConstraintResult validate(const Layer &layer) const noexcept
Validate a layer and return a detailed ConstraintResult.
Definition: constraints.h:1252
garlic::Model::FieldDescriptor::field
field_pointer field
Field instance.
Definition: constraints.h:1164
garlic::ConstraintResult::details
constraint_sequence details
extra details/inner constraint list.
Definition: constraints.h:28
garlic::field_tag
Constraint Tag that passes if the specified Field passes the layer.
Definition: constraints.h:1385
garlic::Model::Model
Model()
Create an empty Model.
Definition: constraints.h:1178
garlic::ConstraintResult::is_leaf
bool is_leaf() const noexcept
Definition: constraints.h:37
garlic::Field::validate
ValidationResult validate(const Layer &layer) const noexcept
test the layer with all the constraints in the field.
Definition: constraints.h:1137
garlic::make_constraint
Constraint make_constraint(Args &&... args) noexcept
Create a generic Constraint based on a constraint tag.
Definition: constraints.h:1486
garlic::Field::message
text message() const noexcept
Definition: constraints.h:1118
garlic::Field::set_ignore_details
void set_ignore_details(bool value)
Set whether or not the field should ignore details.
Definition: constraints.h:1115
garlic::constraint_registry
internal::registry< type_tag, range_tag, regex_tag, any_tag, list_tag, tuple_tag, map_tag, all_tag, model_tag, field_tag, string_literal_tag, int_literal_tag, double_literal_tag, bool_literal_tag, null_literal_tag > constraint_registry
Built-in constraint tags.
Definition: constraints.h:1454
garlic::Field::add_constraint
void add_constraint(Constraint &&constraint)
Add a constraint to the Field.
Definition: constraints.h:1092
garlic::list_size
static size_t list_size(Layer &&layer)
Get the size of a list from a layer.
Definition: utility.h:575
garlic::Field::quick_test
bool quick_test(const Layer &layer) const noexcept
perform a quick and efficient test of all constraints in the field.
Definition: constraints.h:1145
garlic::sequence::empty
bool empty() const noexcept
Definition: containers.h:285
garlic::Model::add_field
void add_field(text &&name, field_pointer field, bool required=true)
Add a new field.
Definition: constraints.h:1193
garlic::any_tag
Constraint Tag that passes if any of the inner constraints pass.
Definition: constraints.h:575
garlic::regex_tag
Constraint Tag that passes if a specified regex pattern passes the test.
Definition: constraints.h:539
garlic::test_constraints
void test_constraints(Layer &&value, Container &&constraints, BackInserterIterator it)
Run a number of constraints on a layer.
Definition: constraints.h:343
garlic::Constraint::empty
static Constraint empty() noexcept
Make a Constraint without any context.
Definition: constraints.h:293
garlic::Model::end_fields
const_field_iterator end_fields() const noexcept
Definition: constraints.h:1224
garlic::ConstraintResult::is_field
bool is_field() const noexcept
Definition: constraints.h:43
containers.h
Supporting containers for the garlic model.
garlic::Model::get_field
field_pointer get_field(KeyType &&name) const
Definition: constraints.h:1202
garlic::tuple_tag
Constraint Tag that passes if the layer is a list and its first n elements pass the first n constrain...
Definition: constraints.h:695
garlic::Model::begin_fields
const_field_iterator begin_fields() const noexcept
Definition: constraints.h:1221
garlic::ConstraintResult::reason
text reason
reason for the failure, if any. empty if successful.
Definition: constraints.h:32
garlic::Model::find_field
const_field_iterator find_field(const text &name) const noexcept
Definition: constraints.h:1227
garlic::ConstraintResult::is_valid
bool is_valid() const noexcept
Definition: constraints.h:40
garlic::Constraint::quick_test
bool quick_test(const Layer &value) const noexcept
Very similar to Constraint::test() but it only returns a boolean.
Definition: constraints.h:1474
garlic::test_constraints_quick
static bool test_constraints_quick(Layer &&value, Container &&constraints)
Definition: constraints.h:363
garlic::Model::annotations
const auto & annotations() const noexcept
Definition: constraints.h:1214
garlic::all_tag
Constraint Tag that passes if all inner constraints pass the layer.
Definition: constraints.h:899
garlic::Field::ValidationResult::failures
constraint_sequence failures
all failed constraints.
Definition: constraints.h:1056
garlic::Field::annotations
const auto & annotations() const noexcept
Definition: constraints.h:1107
garlic::Field::name
const text & name() const noexcept
Definition: constraints.h:1126
garlic::Model
An object to describe a Model.
Definition: constraints.h:1157
garlic::Model::annotations
auto & annotations() noexcept
Definition: constraints.h:1211
garlic::Field::end_constraints
const_constraint_iterator end_constraints() const noexcept
Definition: constraints.h:1133
garlic::sequence::size
SizeType size() const noexcept
Definition: containers.h:279
garlic::test_constraints_first_failure
static ConstraintResult test_constraints_first_failure(Layer &&value, Container &&constraints)
Definition: constraints.h:377
garlic::ConstraintResult::name
text name
name of the constraint. empty if successful.
Definition: constraints.h:30
garlic::Field::ignore_details
bool ignore_details() const
Definition: constraints.h:1112
garlic::Model::name
const text & name() const noexcept
Definition: constraints.h:1217
garlic::sequence< ConstraintResult >
garlic::Field
A named group of Constraint elements.
Definition: constraints.h:1047
garlic::Field::ValidationResult
List of all failed constraints.
Definition: constraints.h:1053
garlic::type_tag
Constraint Tag that passes if the layer has matching data type.
Definition: constraints.h:391
garlic::ConstraintResult
Result of a constriant test.
Definition: constraints.h:18