|
|
@@ -44,12 +44,22 @@
|
|
|
} \
|
|
|
} while (false)
|
|
|
|
|
|
+#define ANNOTATION_HELPER(name, ADD, FMT) \
|
|
|
+ void name(auto const &... args) const { \
|
|
|
+ if (not result_) { \
|
|
|
+ /* do nothing if there's no result object to append to */ \
|
|
|
+ } else if (schema_path_.empty()) { \
|
|
|
+ result_->ADD(where_, schema_path_, "", FMT(args...)); \
|
|
|
+ } else { \
|
|
|
+ result_->ADD(where_, schema_path_.parent(), schema_path_.back(), FMT(args...)); \
|
|
|
+ } \
|
|
|
+ }
|
|
|
+
|
|
|
namespace jvalidate {
|
|
|
template <Adapter Root, RegexEngine RE, typename ExtensionVisitor> class ValidationVisitor {
|
|
|
private:
|
|
|
JVALIDATE_TRIBOOL_TYPE(StoreResults, ForValid, ForInvalid, ForAnything);
|
|
|
using VisitedAnnotation = std::tuple<std::unordered_set<size_t>, std::unordered_set<std::string>>;
|
|
|
- friend ExtensionVisitor;
|
|
|
|
|
|
private:
|
|
|
detail::Pointer where_;
|
|
|
@@ -84,6 +94,7 @@ public:
|
|
|
: schema_(&schema), root_(&root), result_(result), cfg_(cfg), extension_(extension),
|
|
|
regex_(regex) {}
|
|
|
|
|
|
+private:
|
|
|
Status visit(constraint::ExtensionConstraint const & cons, Adapter auto const & document) const {
|
|
|
// Because we don't provide any contract constraint on our ExtensionVisitor,
|
|
|
// we instead defer it to here where we validate that the extension can be
|
|
|
@@ -560,8 +571,8 @@ public:
|
|
|
return rval;
|
|
|
}
|
|
|
|
|
|
- template <Adapter A>
|
|
|
- Status visit(constraint::PropertyNamesConstraint const & cons, A const & document) const {
|
|
|
+ Status visit(constraint::PropertyNamesConstraint const & cons,
|
|
|
+ Adapter auto const & document) const {
|
|
|
NOOP_UNLESS_TYPE(Object);
|
|
|
|
|
|
Status rval = Status::Accept;
|
|
|
@@ -627,9 +638,12 @@ public:
|
|
|
return rval;
|
|
|
}
|
|
|
|
|
|
+public:
|
|
|
/**
|
|
|
* @brief The main entry point into the validator. Validates the provided
|
|
|
- * document according to the schema.
|
|
|
+ * document according to the schema. This function should only be called
|
|
|
+ * internally (validate_subschema/validate_subschema_on) or via the Validator
|
|
|
+ * class.
|
|
|
*/
|
|
|
Status validate(Adapter auto const & document) {
|
|
|
// Step 1) Check if this is an always-false schema. Sometimes, this will
|
|
|
@@ -694,33 +708,40 @@ public:
|
|
|
return rval;
|
|
|
}
|
|
|
|
|
|
-private:
|
|
|
- template <typename S>
|
|
|
- requires(std::is_constructible_v<std::string, S>)
|
|
|
- // Optimization to avoid running string-like objects through a
|
|
|
- // std::stringstream in fmtlist.
|
|
|
- static std::string fmt(S const & str) {
|
|
|
- return std::string(str);
|
|
|
- }
|
|
|
+ // Functions to grant access to some, but not all of the internals of the
|
|
|
+ // ValidationVisitor for the purposes of implementing ExtensionVisitor
|
|
|
+ // validators.
|
|
|
+ detail::Pointer const & where() const { return where_; }
|
|
|
+ Root const & root() const { return *root_; }
|
|
|
+ ValidationConfig const & config() const { return cfg_; }
|
|
|
+ RE & regex() const { return regex_; }
|
|
|
|
|
|
- // Format va_args into a single string to annotate or mark an error message
|
|
|
- static std::string fmt(auto const &... args) {
|
|
|
- std::stringstream ss;
|
|
|
- using ::jvalidate::operator<<;
|
|
|
- [[maybe_unused]] int _[] = {(ss << args, 0)...};
|
|
|
- return ss.str();
|
|
|
+ /**
|
|
|
+ * @brief Allow ExtensionVisitor to enter a state similar to a NotConstraint
|
|
|
+ * when calling sub-requests.
|
|
|
+ *
|
|
|
+ * @return A ScopedState object that will restore the tracking mode once it
|
|
|
+ * is destroyed.
|
|
|
+ */
|
|
|
+ [[nodiscard]] detail::ScopedState invert_tracking() const {
|
|
|
+ return detail::ScopedState(tracking_, !tracking_);
|
|
|
}
|
|
|
|
|
|
- // Format an iterable argument into a vector of strings to annotate or mark
|
|
|
- // an error.
|
|
|
- static std::vector<std::string> fmtlist(auto const & arg) {
|
|
|
- std::vector<std::string> strs;
|
|
|
- for (auto const & elem : arg) {
|
|
|
- strs.push_back(fmt(elem));
|
|
|
- }
|
|
|
- return strs;
|
|
|
+ /**
|
|
|
+ * @brief Allow ExtensionVisitor to enable tracking of all results in child
|
|
|
+ * constraints.
|
|
|
+ *
|
|
|
+ * @return A ScopedState object that will restore the tracking mode once it
|
|
|
+ * is destroyed.
|
|
|
+ */
|
|
|
+ [[nodiscard]] detail::ScopedState track_everything() const {
|
|
|
+ return detail::ScopedState(tracking_, StoreResults::ForAnything);
|
|
|
}
|
|
|
|
|
|
+ ANNOTATION_HELPER(error, error, fmt)
|
|
|
+ ANNOTATION_HELPER(annotate, annotate, fmt)
|
|
|
+ ANNOTATION_HELPER(annotate_list, annotate, fmtlist)
|
|
|
+
|
|
|
bool should_annotate(Status stat) const {
|
|
|
if (not result_) {
|
|
|
return false;
|
|
|
@@ -735,21 +756,6 @@ private:
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-#define ANNOTATION_HELPER(name, ADD, FMT) \
|
|
|
- void name(auto const &... args) const { \
|
|
|
- if (not result_) { \
|
|
|
- /* do nothing if there's no result object to append to */ \
|
|
|
- } else if (schema_path_.empty()) { \
|
|
|
- result_->ADD(where_, schema_path_, "", FMT(args...)); \
|
|
|
- } else { \
|
|
|
- result_->ADD(where_, schema_path_.parent(), schema_path_.back(), FMT(args...)); \
|
|
|
- } \
|
|
|
- }
|
|
|
-
|
|
|
- ANNOTATION_HELPER(error, error, fmt)
|
|
|
- ANNOTATION_HELPER(annotate, annotate, fmt)
|
|
|
- ANNOTATION_HELPER(annotate_list, annotate, fmtlist)
|
|
|
-
|
|
|
Status result(Status stat, auto const &... args) const {
|
|
|
return (should_annotate(stat) ? error(args...) : void(), stat);
|
|
|
}
|
|
|
@@ -845,5 +851,38 @@ private:
|
|
|
}
|
|
|
return rval;
|
|
|
}
|
|
|
+
|
|
|
+private:
|
|
|
+ template <typename S>
|
|
|
+ requires(std::is_constructible_v<std::string, S>)
|
|
|
+ // Optimization to avoid running string-like objects through a
|
|
|
+ // std::stringstream in fmtlist.
|
|
|
+ static std::string fmt(S const & str) {
|
|
|
+ return std::string(str);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Format va_args into a single string to annotate or mark an error message
|
|
|
+ static std::string fmt(auto const &... args) {
|
|
|
+ std::stringstream ss;
|
|
|
+ using ::jvalidate::operator<<;
|
|
|
+ [[maybe_unused]] int _[] = {(ss << args, 0)...};
|
|
|
+ return ss.str();
|
|
|
+ }
|
|
|
+
|
|
|
+ // Format an iterable argument into a vector of strings to annotate or mark
|
|
|
+ // an error.
|
|
|
+ static std::vector<std::string> fmtlist(auto const & arg) {
|
|
|
+ std::vector<std::string> strs;
|
|
|
+ for (auto const & elem : arg) {
|
|
|
+ strs.push_back(fmt(elem));
|
|
|
+ }
|
|
|
+ return strs;
|
|
|
+ }
|
|
|
};
|
|
|
}
|
|
|
+
|
|
|
+#undef ANNOTATION_HELPER
|
|
|
+#undef BREAK_EARLY_IF_NO_RESULT_TREE
|
|
|
+#undef NOOP_UNLESS_TYPE
|
|
|
+#undef VALIDATE_SUBSCHEMA_AND_MARK_LOCAL_VISIT
|
|
|
+#undef VISITED
|