Ver Fonte

refactor: add more error messages

Sam Jaffe há 1 ano atrás
pai
commit
f1e2c717b3

+ 6 - 10
include/jvalidate/constraint/array_constraint.h

@@ -34,23 +34,19 @@ public:
 };
 
 class MaxItemsConstraint : public SimpleConstraint<MaxItemsConstraint> {
-private:
-  int64_t value_;
-
 public:
-  MaxItemsConstraint(int64_t value) : value_(value) {}
+  int64_t value;
 
-  bool operator()(ArrayAdapter auto const & arg) const { return arg.size() <= value_; }
+public:
+  MaxItemsConstraint(int64_t value) : value(value) {}
 };
 
 class MinItemsConstraint : public SimpleConstraint<MinItemsConstraint> {
-private:
-  int64_t value_;
-
 public:
-  MinItemsConstraint(int64_t value) : value_(value) {}
+  int64_t value;
 
-  bool operator()(ArrayAdapter auto const & arg) const { return arg.size() >= value_; }
+public:
+  MinItemsConstraint(int64_t value) : value(value) {}
 };
 
 class TupleConstraint : public SimpleConstraint<TupleConstraint> {

+ 15 - 14
include/jvalidate/constraint/number_constraint.h

@@ -10,39 +10,40 @@
 
 namespace jvalidate::constraint {
 class MaximumConstraint : public SimpleConstraint<MaximumConstraint> {
-private:
-  double value_;
-  bool exclusive_;
+public:
+  double value;
+  bool exclusive;
 
 public:
-  MaximumConstraint(double value, bool exclusive) : value_(value), exclusive_(exclusive) {}
+  MaximumConstraint(double value, bool exclusive) : value(value), exclusive(exclusive) {}
 
-  bool operator()(double arg) const { return exclusive_ ? arg < value_ : arg <= value_; }
+  bool operator()(double arg) const { return exclusive ? arg < value : arg <= value; }
 };
 
 class MinimumConstraint : public SimpleConstraint<MinimumConstraint> {
-private:
-  double value_;
-  bool exclusive_;
+public:
+  double value;
+  bool exclusive;
 
 public:
-  MinimumConstraint(double value, bool exclusive) : value_(value), exclusive_(exclusive) {}
+  MinimumConstraint(double value, bool exclusive) : value(value), exclusive(exclusive) {}
 
-  bool operator()(double arg) const { return exclusive_ ? arg > value_ : arg >= value_; }
+  bool operator()(double arg) const { return exclusive ? arg > value : arg >= value; }
 };
 
 class MultipleOfConstraint : public SimpleConstraint<MultipleOfConstraint> {
-private:
-  double value_;
+public:
+  double value;
 
 public:
-  MultipleOfConstraint(double value) : value_(value) {}
+  MultipleOfConstraint(double value) : value(value) {}
 
   bool operator()(double arg) const {
+    // TODO(samjaffe): ...
     if (std::isinf(arg)) {
       return false;
     }
-    auto val = arg / value_;
+    auto val = arg / value;
     return std::abs(std::floor(val) - val) < std::numeric_limits<double>::epsilon();
   }
 };

+ 6 - 10
include/jvalidate/constraint/object_constraint.h

@@ -42,23 +42,19 @@ public:
 };
 
 class MaxPropertiesConstraint : public SimpleConstraint<MaxPropertiesConstraint> {
-private:
-  int64_t value_;
-
 public:
-  MaxPropertiesConstraint(int64_t value) : value_(value) {}
+  int64_t value;
 
-  bool operator()(ObjectAdapter auto const & arg) const { return arg.size() <= value_; }
+public:
+  MaxPropertiesConstraint(int64_t value) : value(value) {}
 };
 
 class MinPropertiesConstraint : public SimpleConstraint<MinPropertiesConstraint> {
-private:
-  int64_t value_;
-
 public:
-  MinPropertiesConstraint(int64_t value) : value_(value) {}
+  int64_t value;
 
-  bool operator()(ObjectAdapter auto const & arg) const { return arg.size() >= value_; }
+public:
+  MinPropertiesConstraint(int64_t value) : value(value) {}
 };
 
 class PatternPropertiesConstraint : public SimpleConstraint<PatternPropertiesConstraint> {

+ 6 - 10
include/jvalidate/constraint/string_constraint.h

@@ -8,23 +8,19 @@
 
 namespace jvalidate::constraint {
 class MinLengthConstraint : public SimpleConstraint<MinLengthConstraint> {
-private:
-  int64_t value_;
-
 public:
-  MinLengthConstraint(int64_t value) : value_(value) {}
+  int64_t value;
 
-  bool operator()(std::string_view arg) const { return detail::length(arg) >= value_; }
+public:
+  MinLengthConstraint(int64_t value) : value(value) {}
 };
 
 class MaxLengthConstraint : public SimpleConstraint<MaxLengthConstraint> {
-private:
-  int64_t value_;
-
 public:
-  MaxLengthConstraint(int64_t value) : value_(value) {}
+  int64_t value;
 
-  bool operator()(std::string_view arg) const { return detail::length(arg) <= value_; }
+public:
+  MaxLengthConstraint(int64_t value) : value(value) {}
 };
 
 class PatternConstraint : public SimpleConstraint<PatternConstraint> {

+ 70 - 16
include/jvalidate/validation_visitor.h

@@ -126,13 +126,13 @@ public:
   Status visit(constraint::NotConstraint const & cons) const {
     VisitedAnnotation * suppress = nullptr;
     std::swap(suppress, visited_);
-    auto rval = validate_subschema(cons.child, "") == Status::Reject;
+    auto rval = validate_subschema(cons.child, detail::Pointer()) == Status::Reject;
     std::swap(suppress, visited_);
     return rval;
   }
 
   Status visit(constraint::ConditionalConstraint const & cons) const {
-    if (validate_subschema(cons.if_constraint, "")) {
+    if (validate_subschema(cons.if_constraint, detail::Pointer())) {
       return validate_subschema(cons.then_constraint, "then");
     }
     return validate_subschema(cons.else_constraint, "else");
@@ -141,9 +141,19 @@ public:
   Status visit(constraint::MaximumConstraint const & cons) const {
     switch (document_.type()) {
     case adapter::Type::Integer:
-      return cons(document_.as_integer());
+      if (int64_t value = document_.as_integer(); not cons(value)) {
+        add_error("integer ", value, " exceeds ", cons.exclusive ? "exclusive " : "", "maximum of ",
+                  cons.value);
+        return false;
+      }
+      return true;
     case adapter::Type::Number:
-      return cons(document_.as_number());
+      if (double value = document_.as_number(); not cons(value)) {
+        add_error("number ", value, " exceeds ", cons.exclusive ? "exclusive " : "", "maximum of ",
+                  cons.value);
+        return false;
+      }
+      return true;
     default:
       return Status::Noop;
     }
@@ -152,9 +162,19 @@ public:
   Status visit(constraint::MinimumConstraint const & cons) const {
     switch (document_.type()) {
     case adapter::Type::Integer:
-      return cons(document_.as_integer());
+      if (int64_t value = document_.as_integer(); not cons(value)) {
+        add_error("integer ", value, " fails ", cons.exclusive ? "exclusive " : "", "minimum of ",
+                  cons.value);
+        return false;
+      }
+      return true;
     case adapter::Type::Number:
-      return cons(document_.as_number());
+      if (double value = document_.as_number(); not cons(value)) {
+        add_error("number ", value, " fails ", cons.exclusive ? "exclusive " : "", "minimum of ",
+                  cons.value);
+        return false;
+      }
+      return true;
     default:
       return Status::Noop;
     }
@@ -162,24 +182,40 @@ public:
 
   Status visit(constraint::MultipleOfConstraint const & cons) const {
     NOOP_UNLESS_TYPE(Number);
-    return cons(document_.as_number());
+    if (double value = document_.as_number(); not cons(value)) {
+      add_error("number ", value, " is not a multiple of ", cons.value);
+      return false;
+    }
+    return true;
   }
 
   Status visit(constraint::MaxLengthConstraint const & cons) const {
     NOOP_UNLESS_TYPE(String);
-    return cons(document_.as_string());
+    if (auto str = document_.as_string(); detail::length(str) > cons.value) {
+      add_error("string '", str, "' is greater than the maximum length of ", cons.value);
+      return false;
+    }
+    return true;
   }
 
   Status visit(constraint::MinLengthConstraint const & cons) const {
     NOOP_UNLESS_TYPE(String);
-    return cons(document_.as_string());
+    if (auto str = document_.as_string(); detail::length(str) < cons.value) {
+      add_error("string '", str, "' is less than the minimum length of ", cons.value);
+      return false;
+    }
+    return true;
   }
 
   Status visit(constraint::PatternConstraint const & cons) const {
     NOOP_UNLESS_TYPE(String);
 
     RE const & regex = regex_cache_.try_emplace(cons.regex, cons.regex).first->second;
-    return regex.search(document_.as_string());
+    if (auto str = document_.as_string(); not regex.search(str)) {
+      add_error("string '", str, "' does not match pattern /", cons.regex, "/");
+      return false;
+    }
+    return true;
   }
 
   Status visit(constraint::AdditionalItemsConstraint const & cons) const {
@@ -210,9 +246,11 @@ public:
     }
 
     if (matches < minimum) {
+      add_error("array does not contain at least ", minimum, " matching elements");
       return Status::Reject;
     }
     if (matches > maximum) {
+      add_error("array contains more than ", maximum, " matching elements");
       return Status::Reject;
     }
     return Status::Accept;
@@ -220,12 +258,20 @@ public:
 
   Status visit(constraint::MaxItemsConstraint const & cons) const {
     NOOP_UNLESS_TYPE(Array);
-    return cons(document_.as_array());
+    if (auto size = document_.array_size(); size > cons.value) {
+      add_error("array with ", size, " items is greater than the maximum of ", cons.value);
+      return false;
+    }
+    return true;
   }
 
   Status visit(constraint::MinItemsConstraint const & cons) const {
     NOOP_UNLESS_TYPE(Array);
-    return cons(document_.as_array());
+    if (auto size = document_.array_size(); size < cons.value) {
+      add_error("array with ", size, " items is less than the minimum of ", cons.value);
+      return false;
+    }
+    return true;
   }
 
   Status visit(constraint::TupleConstraint const & cons) const {
@@ -250,6 +296,7 @@ public:
       std::set<A> cache;
       for (A const & elem : document_.as_array()) {
         if (not cache.insert(elem).second) {
+          add_error("array contains duplicate elements");
           return Status::Reject;
         }
       }
@@ -258,6 +305,7 @@ public:
       for (size_t i = 0; i < array.size(); ++i) {
         for (size_t j = i + 1; j < array.size(); ++j) {
           if (array[i].equals(array[j], true)) {
+            add_error("array elements ", i, " and ", j, " are equal");
             return Status::Reject;
           }
         }
@@ -323,12 +371,20 @@ public:
 
   Status visit(constraint::MaxPropertiesConstraint const & cons) const {
     NOOP_UNLESS_TYPE(Object);
-    return cons(document_.as_object());
+    if (auto size = document_.object_size(); size > cons.value) {
+      add_error("object with ", size, " properties is greater than the maximum of ", cons.value);
+      return false;
+    }
+    return true;
   }
 
   Status visit(constraint::MinPropertiesConstraint const & cons) const {
     NOOP_UNLESS_TYPE(Object);
-    return cons(document_.as_object());
+    if (auto size = document_.object_size(); size < cons.value) {
+      add_error("object with ", size, " properties is less than the minimum of ", cons.value);
+      return false;
+    }
+    return true;
   }
 
   Status visit(constraint::PatternPropertiesConstraint const & cons) const {
@@ -490,8 +546,6 @@ private:
 
   template <typename K>
   Status validate_subschema(schema::Node const * subschema, K const & key) const {
-    EXPECT(subschema != schema_); // TODO(samjaffe) - Figure out what's causing this infinite loop
-
     VisitedAnnotation annotate;
 
     ValidationVisitor next = *this;