|
@@ -18,6 +18,13 @@
|
|
|
#define NOOP_UNLESS_TYPE(etype) \
|
|
#define NOOP_UNLESS_TYPE(etype) \
|
|
|
RETURN_UNLESS(document_.type() == adapter::Type::etype, Status::Noop)
|
|
RETURN_UNLESS(document_.type() == adapter::Type::etype, Status::Noop)
|
|
|
|
|
|
|
|
|
|
+#define BREAK_EARLY_IF_NO_RESULT_TREE() \
|
|
|
|
|
+ do { \
|
|
|
|
|
+ if (rval == Status::Reject and not result_) { \
|
|
|
|
|
+ break; \
|
|
|
|
|
+ } \
|
|
|
|
|
+ } while (false)
|
|
|
|
|
+
|
|
|
namespace jvalidate {
|
|
namespace jvalidate {
|
|
|
template <Adapter A, RegexEngine RE>
|
|
template <Adapter A, RegexEngine RE>
|
|
|
class ValidationVisitor : public constraint::ConstraintVisitor {
|
|
class ValidationVisitor : public constraint::ConstraintVisitor {
|
|
@@ -69,9 +76,7 @@ public:
|
|
|
|
|
|
|
|
for (schema::Node const * subschema : cons.children) {
|
|
for (schema::Node const * subschema : cons.children) {
|
|
|
rval &= validate_subschema(subschema);
|
|
rval &= validate_subschema(subschema);
|
|
|
- if (!rval && result_ == nullptr) {
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ BREAK_EARLY_IF_NO_RESULT_TREE();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return rval;
|
|
return rval;
|
|
@@ -161,9 +166,7 @@ public:
|
|
|
Status rval = Status::Accept;
|
|
Status rval = Status::Accept;
|
|
|
for (size_t i = cons.applies_after_nth; i < array.size(); ++i) {
|
|
for (size_t i = cons.applies_after_nth; i < array.size(); ++i) {
|
|
|
rval &= validate_subschema_on(cons.subschema, array[i], i);
|
|
rval &= validate_subschema_on(cons.subschema, array[i], i);
|
|
|
- if (rval == Status::Reject && result_ == nullptr) {
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ BREAK_EARLY_IF_NO_RESULT_TREE();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return rval;
|
|
return rval;
|
|
@@ -210,9 +213,7 @@ public:
|
|
|
size_t const n = std::min(cons.items.size(), array.size());
|
|
size_t const n = std::min(cons.items.size(), array.size());
|
|
|
for (size_t i = 0; i < n; ++i) {
|
|
for (size_t i = 0; i < n; ++i) {
|
|
|
rval &= validate_subschema_on(cons.items[i], array[i], i);
|
|
rval &= validate_subschema_on(cons.items[i], array[i], i);
|
|
|
- if (rval == Status::Reject && result_ == nullptr) {
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ BREAK_EARLY_IF_NO_RESULT_TREE();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return rval;
|
|
return rval;
|
|
@@ -260,9 +261,7 @@ public:
|
|
|
if (not cons.properties.contains(key) && not matches_any_pattern(key)) {
|
|
if (not cons.properties.contains(key) && not matches_any_pattern(key)) {
|
|
|
rval &= validate_subschema_on(cons.subschema, elem, key);
|
|
rval &= validate_subschema_on(cons.subschema, elem, key);
|
|
|
}
|
|
}
|
|
|
- if (rval == Status::Reject && result_ == nullptr) {
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ BREAK_EARLY_IF_NO_RESULT_TREE();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return rval;
|
|
return rval;
|
|
@@ -279,9 +278,7 @@ public:
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
rval &= validate_subschema(subschema);
|
|
rval &= validate_subschema(subschema);
|
|
|
- if (rval == Status::Reject && result_ == nullptr) {
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ BREAK_EARLY_IF_NO_RESULT_TREE();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
for (auto [key, required] : cons.required) {
|
|
for (auto [key, required] : cons.required) {
|
|
@@ -294,9 +291,7 @@ public:
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
rval &= required.empty();
|
|
rval &= required.empty();
|
|
|
- if (rval == Status::Reject && result_ == nullptr) {
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ BREAK_EARLY_IF_NO_RESULT_TREE();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return rval;
|
|
return rval;
|
|
@@ -322,9 +317,7 @@ public:
|
|
|
if (regex.search(key)) {
|
|
if (regex.search(key)) {
|
|
|
rval &= validate_subschema_on(subschema, elem, key);
|
|
rval &= validate_subschema_on(subschema, elem, key);
|
|
|
}
|
|
}
|
|
|
- if (rval == Status::Reject && result_ == nullptr) {
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ BREAK_EARLY_IF_NO_RESULT_TREE();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -350,9 +343,7 @@ public:
|
|
|
if (auto it = cons.properties.find(key); it != cons.properties.end()) {
|
|
if (auto it = cons.properties.find(key); it != cons.properties.end()) {
|
|
|
rval &= validate_subschema_on(it->second, elem, key);
|
|
rval &= validate_subschema_on(it->second, elem, key);
|
|
|
}
|
|
}
|
|
|
- if (rval == Status::Reject && result_ == nullptr) {
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ BREAK_EARLY_IF_NO_RESULT_TREE();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return rval;
|
|
return rval;
|
|
@@ -388,12 +379,10 @@ public:
|
|
|
Status rval = Status::Accept;
|
|
Status rval = Status::Accept;
|
|
|
auto array = document_.as_array();
|
|
auto array = document_.as_array();
|
|
|
for (size_t i = 0; i < array.size(); ++i) {
|
|
for (size_t i = 0; i < array.size(); ++i) {
|
|
|
- if (not local_result_->visited_items.contains(i)) {
|
|
|
|
|
|
|
+ if (not local_result_->has_visited(i)) {
|
|
|
rval &= validate_subschema_on(cons.subschema, array[i], i);
|
|
rval &= validate_subschema_on(cons.subschema, array[i], i);
|
|
|
}
|
|
}
|
|
|
- if (rval == Status::Reject && result_ == nullptr) {
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ BREAK_EARLY_IF_NO_RESULT_TREE();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -403,12 +392,10 @@ public:
|
|
|
|
|
|
|
|
Status rval = Status::Accept;
|
|
Status rval = Status::Accept;
|
|
|
for (auto const & [key, elem] : document_.as_object()) {
|
|
for (auto const & [key, elem] : document_.as_object()) {
|
|
|
- if (not local_result_->visited_properties.contains(key)) {
|
|
|
|
|
|
|
+ if (not local_result_->has_visited(key)) {
|
|
|
rval &= validate_subschema_on(cons.subschema, elem, key);
|
|
rval &= validate_subschema_on(cons.subschema, elem, key);
|
|
|
}
|
|
}
|
|
|
- if (rval == Status::Reject && result_ == nullptr) {
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ BREAK_EARLY_IF_NO_RESULT_TREE();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -429,21 +416,34 @@ public:
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
for (auto const & [key, p_constraint] : schema_.constraints()) {
|
|
for (auto const & [key, p_constraint] : schema_.constraints()) {
|
|
|
- if (rval != Status::Reject || result_) {
|
|
|
|
|
- rval &= p_constraint->accept(*this);
|
|
|
|
|
|
|
+ BREAK_EARLY_IF_NO_RESULT_TREE();
|
|
|
|
|
+ if (result_) {
|
|
|
|
|
+ result_->constraint(key);
|
|
|
}
|
|
}
|
|
|
|
|
+ rval &= p_constraint->accept(*this);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
for (auto const & [key, p_constraint] : schema_.post_constraints()) {
|
|
for (auto const & [key, p_constraint] : schema_.post_constraints()) {
|
|
|
- if (rval != Status::Reject || result_) {
|
|
|
|
|
- rval &= p_constraint->accept(*this);
|
|
|
|
|
|
|
+ BREAK_EARLY_IF_NO_RESULT_TREE();
|
|
|
|
|
+ if (result_) {
|
|
|
|
|
+ result_->constraint(key);
|
|
|
}
|
|
}
|
|
|
|
|
+ rval &= p_constraint->accept(*this);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return rval;
|
|
return rval;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
private:
|
|
|
|
|
+ template <typename... Args> void add_error(Args &&... args) const {
|
|
|
|
|
+ if (not result_) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ std::stringstream ss;
|
|
|
|
|
+ ss << (std::forward<Args>(args) << ...);
|
|
|
|
|
+ result_->message(ss.str());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
ValidationVisitor(A const & json, schema::Node const & schema, ValidationConfig const & cfg,
|
|
ValidationVisitor(A const & json, schema::Node const & schema, ValidationConfig const & cfg,
|
|
|
std::unordered_map<std::string, RE> & regex_cache,
|
|
std::unordered_map<std::string, RE> & regex_cache,
|
|
|
detail::Pointer const & where, ValidationResult * result,
|
|
detail::Pointer const & where, ValidationResult * result,
|
|
@@ -466,7 +466,10 @@ private:
|
|
|
auto status =
|
|
auto status =
|
|
|
ValidationVisitor(document, *subschema, cfg_, regex_cache_, where_ / key, pnext).validate();
|
|
ValidationVisitor(document, *subschema, cfg_, regex_cache_, where_ / key, pnext).validate();
|
|
|
if (status != Status::Noop and local_result_) {
|
|
if (status != Status::Noop and local_result_) {
|
|
|
- local_result_->record(key);
|
|
|
|
|
|
|
+ local_result_->visit(key);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (status == Status::Reject and result_) {
|
|
|
|
|
+ result_->error(key, std::move(next));
|
|
|
}
|
|
}
|
|
|
return status;
|
|
return status;
|
|
|
}
|
|
}
|