Ver Fonte

refactor: add parent for json-pointer, more result messages

Sam Jaffe há 1 ano atrás
pai
commit
43d73e2df2
2 ficheiros alterados com 45 adições e 14 exclusões
  1. 9 0
      include/jvalidate/detail/pointer.h
  2. 36 14
      include/jvalidate/validation_visitor.h

+ 9 - 0
include/jvalidate/detail/pointer.h

@@ -13,6 +13,8 @@
 #include <jvalidate/forward.h>
 
 namespace jvalidate::detail {
+struct parent_t {};
+constexpr parent_t parent;
 
 class Pointer {
 public:
@@ -81,6 +83,13 @@ public:
 
   Pointer operator/(Pointer const & relative) const { return Pointer(*this) /= relative; }
 
+  Pointer & operator/=(parent_t) {
+    tokens_.pop_back();
+    return *this;
+  }
+
+  Pointer operator/(parent_t) const { return parent(); }
+
   Pointer & operator/=(std::string_view key) {
     tokens_.emplace_back(std::string(key));
     return *this;

+ 36 - 14
include/jvalidate/validation_visitor.h

@@ -70,7 +70,7 @@ public:
         return Status::Accept;
       }
     }
-    add_error("type ", type, " is not one of {", cons.types, '}');
+    add_error("type ", type, " is not one of {", cons.types, "}");
     return Status::Reject;
   }
 
@@ -94,13 +94,20 @@ public:
   Status visit(constraint::AllOfConstraint const & cons) const {
     Status rval = Status::Accept;
 
+    std::set<size_t> unmatched;
     size_t i = 0;
     for (schema::Node const * subschema : cons.children) {
-      rval &= validate_subschema(subschema, i);
+      if (auto stat = validate_subschema(subschema, i); stat == Status::Reject) {
+        rval = Status::Reject;
+        unmatched.insert(i);
+      }
       ++i;
       BREAK_EARLY_IF_NO_RESULT_TREE();
     }
 
+    if (rval == Status::Reject) {
+      add_error("does not validate subschemas ", unmatched);
+    }
     return rval;
   }
 
@@ -118,35 +125,51 @@ public:
       ++i;
     }
 
+    if (rval == Status::Reject) {
+      add_error("validates none of the subschemas");
+    }
     return rval;
   }
 
   Status visit(constraint::OneOfConstraint const & cons) const {
-    size_t matches = 0;
+    std::set<size_t> matches;
     size_t i = 0;
     for (schema::Node const * subschema : cons.children) {
       if (validate_subschema(subschema, i)) {
-        ++matches;
+        matches.insert(i);
       }
       ++i;
     }
 
-    return matches == 1 ? Status::Accept : Status::Reject;
+    if (matches.size() == 1) {
+      return Status::Accept;
+    }
+    add_error("validates subschemas ", matches);
+    return Status::Reject;
   }
 
   Status visit(constraint::NotConstraint const & cons) const {
     VisitedAnnotation * suppress = nullptr;
     std::swap(suppress, visited_);
-    auto rval = validate_subschema(cons.child, detail::Pointer()) == Status::Reject;
+    bool const rval = validate_subschema(cons.child) == Status::Reject;
     std::swap(suppress, visited_);
+
+    if (not rval) {
+      add_error("actually validates subschema");
+    }
     return rval;
   }
 
   Status visit(constraint::ConditionalConstraint const & cons) const {
-    if (validate_subschema(cons.if_constraint, detail::Pointer())) {
-      return validate_subschema(cons.then_constraint, "then");
+    VisitedAnnotation * suppress = nullptr;
+    std::swap(suppress, visited_);
+    bool const if_result = validate_subschema(cons.if_constraint);
+    std::swap(suppress, visited_);
+
+    if (if_result) {
+      return validate_subschema(cons.then_constraint, detail::parent, "then");
     }
-    return validate_subschema(cons.else_constraint, "else");
+    return validate_subschema(cons.else_constraint, detail::parent, "else");
   }
 
   Status visit(constraint::MaximumConstraint const & cons) const {
@@ -239,8 +262,7 @@ public:
       return true;
     }
 
-    std::cerr << "Unimplemented constraint format(" << cons.format << ")"
-              << "\n";
+    add_error("unimplemented format assertion: '", cons.format, "'");
     return false;
   }
 
@@ -570,12 +592,12 @@ private:
     to.insert(from.begin(), from.end());
   }
 
-  template <typename K>
-  Status validate_subschema(schema::Node const * subschema, K const & key) const {
+  template <typename... K>
+  Status validate_subschema(schema::Node const * subschema, K const &... keys) const {
     VisitedAnnotation annotate;
 
     ValidationVisitor next = *this;
-    next.schema_path_ /= key;
+    ((next.schema_path_ /= keys), ...);
     std::tie(next.schema_, next.visited_) =
         std::forward_as_tuple(subschema, visited_ ? &annotate : nullptr);