|
|
@@ -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;
|