瀏覽代碼

refactor: proper format constraint handling

Sam Jaffe 1 年之前
父節點
當前提交
594ee7d557

+ 5 - 12
include/jvalidate/constraint.h

@@ -65,9 +65,7 @@ private:
       {"exclusiveMaximum", {{schema::Version::Draft06, &Self::exclusiveMaximum}}},
       {"exclusiveMinimum", {{schema::Version::Draft06, &Self::exclusiveMinimum}}},
       {"format",
-       {{schema::Version::Draft04, &Self::warnUnimplemented},
-        {schema::Version::Draft2020_12, nullptr}}},
-      {"format-assertion", {{schema::Version::Draft2020_12, &Self::fatalUnimplemented}}},
+       {{schema::Version::Draft04, &Self::format}, {schema::Version::Draft2020_12, nullptr}}},
       {"if", {{schema::Version::Draft07, &Self::ifThenElse}}},
       {"items",
        {{schema::Version::Draft04, &Self::itemsTupleOrVector},
@@ -150,15 +148,6 @@ public:
 
   // SECTION: Untyped Constraints
 
-  static pConstraint warnUnimplemented(detail::ParserContext<A> const & context) {
-    std::cerr << "Unimplemented constraint " << context.where << "\n";
-    return nullptr;
-  }
-
-  static pConstraint fatalUnimplemented(detail::ParserContext<A> const & context) {
-    JVALIDATE_THROW(std::runtime_error, "Unimplemented constraint " << context.where);
-  }
-
   static auto type(detail::ParserContext<A> const & context) {
     static std::unordered_map<std::string_view, adapter::Type> const s_type_names{
         {"null", adapter::Type::Null},       {"boolean", adapter::Type::Boolean},
@@ -314,6 +303,10 @@ public:
     return std::make_unique<constraint::PatternConstraint>(context.schema.as_string());
   }
 
+  static auto format(detail::ParserContext<A> const & context) {
+    return std::make_unique<constraint::FormatConstraint>(context.schema.as_string());
+  }
+
   // SECTION: Array Constraints
 
   static auto contains(detail::ParserContext<A> const & context) {

+ 8 - 0
include/jvalidate/constraint/string_constraint.h

@@ -30,4 +30,12 @@ public:
 public:
   PatternConstraint(std::string const & regex) : regex(regex) {}
 };
+
+class FormatConstraint : public SimpleConstraint<FormatConstraint> {
+public:
+  std::string format;
+
+public:
+  FormatConstraint(std::string const & format) : format(format) {}
+};
 }

+ 1 - 0
include/jvalidate/constraint/visitor.h

@@ -23,6 +23,7 @@ struct ConstraintVisitor {
   virtual Status visit(MaxLengthConstraint const &) const = 0;
   virtual Status visit(MinLengthConstraint const &) const = 0;
   virtual Status visit(PatternConstraint const &) const = 0;
+  virtual Status visit(FormatConstraint const &) const = 0;
 
   virtual Status visit(AdditionalItemsConstraint const &) const = 0;
   virtual Status visit(ContainsConstraint const &) const = 0;

+ 1 - 0
include/jvalidate/forward.h

@@ -47,6 +47,7 @@ class MultipleOfConstraint;
 class MaxLengthConstraint;
 class MinLengthConstraint;
 class PatternConstraint;
+class FormatConstraint;
 
 class AdditionalItemsConstraint;
 class ContainsConstraint;

+ 5 - 0
include/jvalidate/validation_config.h

@@ -16,5 +16,10 @@ struct ValidationConfig {
   // PropertiesConstraint has a default associated with it - then we construct
   // that object into the document.
   bool construct_default_values = false;
+
+  // When enabled, we will validate format constraints on the document, instead
+  // of using them purely as annotations.
+  // TODO(samjaffe): A schema can also indicate this using $vocabulary
+  bool validate_format = false;
 };
 }

+ 16 - 2
include/jvalidate/validation_visitor.h

@@ -26,7 +26,7 @@
 
 #define BREAK_EARLY_IF_NO_RESULT_TREE()                                                            \
   do {                                                                                             \
-    if (rval == Status::Reject and not result_) {                                                  \
+    if (rval == Status::Reject and not result_ and not visited_) {                                 \
       break;                                                                                       \
     }                                                                                              \
   } while (false)
@@ -110,9 +110,11 @@ public:
     Status rval = Status::Reject;
     for (schema::Node const * subschema : cons.children) {
       if (validate_subschema(subschema, i)) {
-        // TODO(samjaffe): Perhaps we should store some UnevaluatedTracking object for short-circuit
         rval = Status::Accept;
       }
+      if (not visited_ && rval == Status::Accept) {
+        break;
+      }
       ++i;
     }
 
@@ -229,6 +231,18 @@ public:
     return true;
   }
 
+  Status visit(constraint::FormatConstraint const & cons) const {
+    NOOP_UNLESS_TYPE(String);
+
+    if (not cfg_.validate_format) {
+      return true;
+    }
+
+    std::cerr << "Unimplemented constraint format(" << cons.format << ")"
+              << "\n";
+    return false;
+  }
+
   Status visit(constraint::AdditionalItemsConstraint const & cons) const {
     NOOP_UNLESS_TYPE(Array);