Переглянути джерело

feat: implement ExtensionVisitor as a plugin

Sam Jaffe 3 місяців тому
батько
коміт
6001316336

+ 3 - 2
include/jvalidate/forward.h

@@ -171,9 +171,10 @@ concept RegexEngine = requires(R & regex) {
 namespace jvalidate {
 template <Adapter A> class ConstraintFactory;
 template <Adapter A> class DocumentCache;
-template <RegexEngine RE> class ValidationVisitor;
 
-template <RegexEngine RE> class Validator;
+template <RegexEngine RE, typename ExtensionVisitor> class ValidationVisitor;
+
+template <RegexEngine RE, typename ExtensionVisitor> class Validator;
 
 template <Adapter A> using URIResolver = bool (*)(URI const &, typename A::value_type &);
 

+ 1 - 1
include/jvalidate/validation_result.h

@@ -13,7 +13,7 @@ namespace jvalidate {
 class ValidationResult {
 public:
   // Only allow ValidationVisitor to construct the elements of a validation result
-  template <RegexEngine> friend class ValidationVisitor;
+  template <RegexEngine, typename> friend class ValidationVisitor;
 
   using DocPointer = detail::Pointer;
   using SchemaPointer = detail::Pointer;

+ 15 - 7
include/jvalidate/validation_visitor.h

@@ -44,10 +44,11 @@
   } while (false)
 
 namespace jvalidate {
-template <RegexEngine RE> class ValidationVisitor {
+template <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_;
@@ -58,6 +59,7 @@ private:
   ValidationResult * result_;
 
   ValidationConfig const & cfg_;
+  ExtensionVisitor extension_;
   RE & regex_;
 
   mutable VisitedAnnotation * visited_ = nullptr;
@@ -71,12 +73,22 @@ public:
    * @param cfg General configuration settings for how the run is executed
    * @param regex A cache of string regular expressions to compiled
    * regular expressions
+   * @param[optional] extension A special visitor for extension constraints.
    * @param[optional] result A cache of result/annotation info for the user to
    * receive a detailed summary of why a document is supported/unsupported.
    */
   ValidationVisitor(schema::Node const & schema, ValidationConfig const & cfg, RE & regex,
-                    ValidationResult * result)
-      : schema_(&schema), result_(result), cfg_(cfg), regex_(regex) {}
+                    ExtensionVisitor extension, ValidationResult * result)
+      : schema_(&schema), result_(result), cfg_(cfg), extension_(extension), regex_(regex) {}
+
+  Status visit(constraint::ExtensionConstraint const & cons, Adapter auto const & document) const {
+    if constexpr (std::is_invocable_r_v<Status, ExtensionVisitor, decltype(cons),
+                                        decltype(document), ValidationVisitor const &>) {
+      return extension_(cons, document, *this);
+    }
+    annotate("unsupported extension");
+    return Status::Noop;
+  }
 
   Status visit(constraint::TypeConstraint const & cons, Adapter auto const & document) const {
     adapter::Type const type = document.type();
@@ -96,10 +108,6 @@ public:
     return result(Status::Reject, type, " is not in types [", cons.types, "]");
   }
 
-  Status visit(constraint::ExtensionConstraint const & cons, Adapter auto const & document) const {
-    // return cons.validate(document, where_, result_);
-  }
-
   Status visit(constraint::EnumConstraint const & cons, Adapter auto const & document) const {
     auto is_equal = [this, &document](auto const & frozen) {
       return document.equals(frozen, cfg_.strict_equality);

+ 18 - 5
include/jvalidate/validator.h

@@ -32,6 +32,12 @@ public:
     return std::regex_search(text, re);
   }
 };
+
+/**
+ * @brief An implementation of an "Extension Constraint Visitor" plugin that
+ * does nothing.
+ */
+struct StubExtensionVisitor {};
 }
 
 namespace jvalidate {
@@ -41,11 +47,14 @@ namespace jvalidate {
  *
  * @tparam RE A type that can be used to solve regular expressions
  */
-template <RegexEngine RE = detail::StdRegexEngine> class Validator {
+template <RegexEngine RE = detail::StdRegexEngine,
+          typename ExtensionVisitor = detail::StubExtensionVisitor>
+class Validator {
 private:
   schema::Node const & schema_;
   ValidationConfig cfg_;
-  RE regex_cache_;
+  ExtensionVisitor extension_;
+  RE regex_;
 
 public:
   /**
@@ -56,7 +65,11 @@ public:
    * @param cfg Any special (runtime) configuration rules being applied to the
    * validator.
    */
-  Validator(schema::Node const & schema, ValidationConfig const & cfg = {})
+  Validator(schema::Node const & schema, ExtensionVisitor extension = {},
+            ValidationConfig const & cfg = {})
+      : schema_(schema), cfg_(cfg), extension_(extension) {}
+
+  Validator(schema::Node const & schema, ValidationConfig const & cfg)
       : schema_(schema), cfg_(cfg) {}
 
   template <typename... Args> Validator(schema::Node &&, Args &&...) = delete;
@@ -80,7 +93,7 @@ public:
     EXPECT_M(not cfg_.construct_default_values,
              "Cannot perform mutations on an immutable JSON Adapter");
     return static_cast<bool>(
-        ValidationVisitor<RE>(schema_, cfg_, regex_cache_, result).validate(json));
+        ValidationVisitor(schema_, cfg_, regex_, extension_, result).validate(json));
   }
 
   /**
@@ -99,7 +112,7 @@ public:
    */
   template <MutableAdapter A> bool validate(A const & json, ValidationResult * result = nullptr) {
     return static_cast<bool>(
-        ValidationVisitor<RE>(schema_, cfg_, regex_cache_, result).validate(json));
+        ValidationVisitor(schema_, cfg_, regex_, extension_, result).validate(json));
   }
 
   /**