|
|
@@ -18,24 +18,28 @@
|
|
|
RETURN_UNLESS(document_.type() == adapter::Type::etype, Status::Noop)
|
|
|
|
|
|
namespace jvalidate {
|
|
|
-template <Adapter A, RegexEngine RE, typename CRTP, constraint::Extension... Extensions>
|
|
|
-class ValidationVisitorBase : public constraint::ConstraintVisitor,
|
|
|
- public constraint::ExtensionConstraintVisitor<Extensions>... {
|
|
|
+template <Adapter A, RegexEngine RE>
|
|
|
+class ValidationVisitor : public constraint::ConstraintVisitor {
|
|
|
private:
|
|
|
A document_;
|
|
|
+ detail::Pointer where_;
|
|
|
schema::Node const & schema_;
|
|
|
Result * result_;
|
|
|
std::unordered_map<std::string, RE> & regex_cache_;
|
|
|
|
|
|
public:
|
|
|
- ValidationVisitorBase(A const & json, schema::Node const & schema,
|
|
|
- std::unordered_map<std::string, RE> & regex_cache, Result * result)
|
|
|
- : document_(json), schema_(schema), result_(result) {}
|
|
|
+ ValidationVisitor(A const & json, schema::Node const & schema,
|
|
|
+ std::unordered_map<std::string, RE> & regex_cache, Result * result)
|
|
|
+ : ValidationVisitor(json, schema, {}, regex_cache, result) {}
|
|
|
|
|
|
Status visit(constraint::TypeConstraint const & cons) const {
|
|
|
return cons.types.contains(document_.type());
|
|
|
}
|
|
|
|
|
|
+ Status visit(constraint::ExtensionConstraint const & cons) const {
|
|
|
+ return cons.validate(document_, where_, result_);
|
|
|
+ }
|
|
|
+
|
|
|
Status visit(constraint::EnumConstraint const & cons) const {
|
|
|
for (auto const & option : cons.enumeration) {
|
|
|
// TODO(samjaffe) Strictness
|
|
|
@@ -142,7 +146,7 @@ public:
|
|
|
|
|
|
Status rval = Status::Accept;
|
|
|
for (size_t i = cons.applies_after_nth; i < array.size(); ++i) {
|
|
|
- rval &= validate_subschema_on(cons.subschema, array[i]);
|
|
|
+ rval &= validate_subschema_on(cons.subschema, array[i], i);
|
|
|
if (!rval && result_ == nullptr) {
|
|
|
break;
|
|
|
}
|
|
|
@@ -158,8 +162,8 @@ public:
|
|
|
size_t const minimum = cons.minimum.value_or(1);
|
|
|
size_t const maximum = cons.maximum.value_or(array.size());
|
|
|
size_t matches = 0;
|
|
|
- for (A const & elem : array) {
|
|
|
- if (validate_subschema_on(cons.subschema, elem)) {
|
|
|
+ for (size_t i = 0; i < array.size(); ++i) {
|
|
|
+ if (validate_subschema_on(cons.subschema, array[i], i)) {
|
|
|
++matches;
|
|
|
}
|
|
|
}
|
|
|
@@ -193,7 +197,7 @@ public:
|
|
|
|
|
|
Status rval = Status::Accept;
|
|
|
for (size_t i = 0; i < cons.items.size(); ++i) {
|
|
|
- rval &= validate_subschema_on(cons.items[i], array[i]);
|
|
|
+ rval &= validate_subschema_on(cons.items[i], array[i], i);
|
|
|
if (!rval && result_ == nullptr) {
|
|
|
break;
|
|
|
}
|
|
|
@@ -242,7 +246,7 @@ public:
|
|
|
Status rval = Status::Accept;
|
|
|
for (auto const & [key, elem] : document_.as_object()) {
|
|
|
if (not cons.properties.contains(key) && not matches_any_pattern(key)) {
|
|
|
- rval &= validate_subschema_on(cons.subschema, elem);
|
|
|
+ rval &= validate_subschema_on(cons.subschema, elem, key);
|
|
|
}
|
|
|
if (!rval && result_ == nullptr) {
|
|
|
break;
|
|
|
@@ -304,7 +308,7 @@ public:
|
|
|
RE const & regex = regex_cache_.try_emplace(pattern, pattern).first->second;
|
|
|
for (auto const & [key, elem] : document_.as_object()) {
|
|
|
if (regex.search(key)) {
|
|
|
- rval &= validate_subschema_on(subschema, elem);
|
|
|
+ rval &= validate_subschema_on(subschema, elem, key);
|
|
|
}
|
|
|
if (!rval && result_ == nullptr) {
|
|
|
break;
|
|
|
@@ -321,7 +325,7 @@ public:
|
|
|
Status rval = Status::Accept;
|
|
|
for (auto const & [key, elem] : document_.as_object()) {
|
|
|
if (auto it = cons.properties.find(key); it != cons.properties.end()) {
|
|
|
- rval &= validate_subschema_on(it->second, elem);
|
|
|
+ rval &= validate_subschema_on(it->second, elem, key);
|
|
|
}
|
|
|
if (!rval && result_ == nullptr) {
|
|
|
break;
|
|
|
@@ -338,7 +342,7 @@ public:
|
|
|
for (auto const & [key, _] : document_.as_object()) {
|
|
|
// TODO(samjaffe): Should we prefer a std::string adapter like valijson?
|
|
|
typename A::value_type key_json{key};
|
|
|
- rval &= validate_subschema_on(cons.key_schema, A(key_json));
|
|
|
+ rval &= validate_subschema_on(cons.key_schema, A(key_json), "$$key");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -360,7 +364,7 @@ public:
|
|
|
auto array = document_.as_array();
|
|
|
for (size_t i = 0; i < array.size(); ++i) {
|
|
|
if (not result_->visited_items.contains(i)) {
|
|
|
- rval &= validate_subschema_for(cons.subschema, array[i]);
|
|
|
+ rval &= validate_subschema_on(cons.subschema, array[i], i);
|
|
|
}
|
|
|
// TODO(samjaffe): Special Rule
|
|
|
if (!rval && result_ == nullptr) {
|
|
|
@@ -375,7 +379,7 @@ public:
|
|
|
Status rval = Status::Accept;
|
|
|
for (auto const & [key, elem] : document_.as_object()) {
|
|
|
if (not result_->visited_properties.contains(key)) {
|
|
|
- rval &= validate_subschema_for(cons.subschema, elem);
|
|
|
+ rval &= validate_subschema_on(cons.subschema, elem, key);
|
|
|
}
|
|
|
// TODO(samjaffe): Special Rule
|
|
|
if (!rval && result_ == nullptr) {
|
|
|
@@ -415,22 +419,26 @@ public:
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
- Status validate(A const & document) const { return validate_subschema_on(&schema_, document_); }
|
|
|
+ ValidationVisitor(A const & json, detail::Pointer const & where, schema::Node const & schema,
|
|
|
+ std::unordered_map<std::string, RE> & regex_cache, Result * result)
|
|
|
+ : document_(json), where_(where), schema_(schema), regex_cache_(regex_cache),
|
|
|
+ result_(result) {}
|
|
|
|
|
|
- Status validate_subschema(schema::Node const * subschema) const {
|
|
|
- return validate_subschema_on(subschema, document_);
|
|
|
+ template <typename K> Status validate(A const & document, K const & key) const {
|
|
|
+ return validate_subschema_on(&schema_, document_, key);
|
|
|
}
|
|
|
|
|
|
- Status validate_subschema_on(schema::Node const * subschema, A const & document) const {
|
|
|
- // TODO(samjaffe): Result implementation
|
|
|
- Result * pnext = nullptr;
|
|
|
- return CRTP(document, *subschema, regex_cache_, pnext).validate();
|
|
|
+ Status validate_subschema(schema::Node const * subschema) const {
|
|
|
+ // TODO(samjaffe): Propagation for Unevaluated*Constraint
|
|
|
+ return CRTP(document_, *subschema, where_, regex_cache_, result_).validate();
|
|
|
}
|
|
|
-};
|
|
|
|
|
|
-template <Adapter A, RegexEngine RE>
|
|
|
-class ValidationVisitor final : public ValidationVisitorBase<A, RE, ValidationVisitor<A, RE>> {
|
|
|
-public:
|
|
|
- using ValidationVisitor::ValidationVisitorBase::ValidationVisitorBase;
|
|
|
+ template <typename K>
|
|
|
+ Status validate_subschema_on(schema::Node const * subschema, A const & document,
|
|
|
+ K const & key) const {
|
|
|
+ Result next;
|
|
|
+ Result * pnext = result_ ? &next : nullptr;
|
|
|
+ return CRTP(document, *subschema, where_ / key, regex_cache_, pnext).validate();
|
|
|
+ }
|
|
|
};
|
|
|
}
|