浏览代码

refactor: change Pointer/RelativePointer to use expected for the string ctor

Sam Jaffe 3 月之前
父节点
当前提交
8697cb37e5

+ 6 - 25
include/jvalidate/detail/pointer.h

@@ -41,32 +41,13 @@ public:
   Pointer(std::vector<Token> const & tokens) : tokens_(tokens) {}
 
   /**
-   * @brief Parse a JSON-Pointer from a serialized JSON-Pointer-String. In
-   * principle, this should either be a factory function returning an optional/
-   * throwing on error - but we'll generously assume that all JSON-Pointers are
-   * valid - and therefore that an invalidly formatter pointer string will
-   * point to somewhere non-existant (since it will be used in schema handling)
+   * @brief Parse a JSON-Pointer from a serialized JSON-Pointer-String.
+   *
+   * @param path A string representation of the Pointer
+   *
+   * @returns A Pointer, if the string is considered valid, else an error
+   * message describing the problem.
    */
-  Pointer(std::string_view path) {
-    if (path.empty()) {
-      return;
-    }
-
-    // JSON-Pointers are required to start with a '/' although we only enforce
-    // that rule in Reference.
-    path.remove_prefix(1);
-    // The rules of JSON-Pointer is that if a token were to contain a '/' as a
-    // strict character: then that character would be escaped, using the above
-    // rules. We take advantage of string_view's sliding view to make iteration
-    // easy.
-    for (size_t p = path.find('/'); p != std::string::npos;
-         path.remove_prefix(p + 1), p = path.find('/')) {
-      tokens_.push_back(parse_token(std::string(path.substr(0, p))).value());
-    }
-
-    tokens_.push_back(parse_token(std::string(path)).value());
-  }
-
   static expected<Pointer, std::string> parse(std::string_view path) {
     if (path.empty()) {
       return Pointer();

+ 1 - 1
include/jvalidate/detail/reference.h

@@ -126,7 +126,7 @@ public:
     size_t pointer_start = 0;
     root_ = RootReference(ref, pointer_start);
     if (pointer_start != std::string::npos) {
-      pointer_ = ref.substr(pointer_start);
+      pointer_ = Pointer::parse(ref.substr(pointer_start)).value();
     }
   }
 

+ 19 - 8
include/jvalidate/detail/relative_pointer.h

@@ -4,6 +4,7 @@
 #include <string>
 #include <string_view>
 
+#include <jvalidate/compat/expected.h>
 #include <jvalidate/detail/expect.h>
 #include <jvalidate/detail/pointer.h>
 #include <jvalidate/forward.h>
@@ -11,20 +12,30 @@
 namespace jvalidate::detail {
 class RelativePointer {
 public:
-  RelativePointer(std::string_view path) {
+  static expected<RelativePointer, std::string> parse(std::string_view path) {
     if (path == "0") {
-      return;
+      return RelativePointer();
     }
+
+    RelativePointer rval;
     if (auto pos = path.find('/'); pos != path.npos) {
-      pointer_ = Pointer(path.substr(pos));
+      expected ptr = Pointer::parse(path.substr(pos));
+      JVALIDATE_PROPIGATE_UNEXPECTED(ptr);
+      rval.pointer_ = *std::move(ptr);
       path.remove_suffix(path.size() - pos);
-    } else {
-      EXPECT_M(not path.empty() && path.back() == '#',
-               "RelativePointer must end in a pointer, or a '#'");
-      requests_key_ = true;
+    } else if (path.back() == '#') {
+      rval.requests_key_ = true;
       path.remove_suffix(1);
     }
-    parent_steps_ = std::stoull(std::string(path));
+
+    if (path.find_first_not_of("0123456789") != std::string_view::npos) {
+      return unexpected("RelativePointer must end in a pointer, or a '#'");
+    }
+
+    expected parent_steps = Pointer::parse_integer<size_t>(path);
+    JVALIDATE_PROPIGATE_UNEXPECTED(parent_steps);
+    rval.parent_steps_ = *parent_steps;
+    return rval;
   }
 
   template <Adapter A>

+ 2 - 1
tests/extension_test.cxx

@@ -20,7 +20,8 @@ using jvalidate::constraint::ExtensionConstraint;
 using testing::Not;
 
 struct IsKeyOfConstraint : jvalidate::extension::ConstraintBase<IsKeyOfConstraint> {
-  IsKeyOfConstraint(std::string_view ptr) : ptr(ptr) {
+  IsKeyOfConstraint(std::string_view ptr)
+      : ptr(jvalidate::detail::RelativePointer::parse(ptr).value()) {
     EXPECT_M(ptr.find('/') != std::string_view::npos,
              "IsKeyOfConstraint requires a value-relative-pointer, not a key-relative-pointer");
   }

+ 1 - 1
tests/matchers.h

@@ -8,7 +8,7 @@
 #include <json/value.h>
 
 inline auto operator""_jptr(char const * data, size_t len) {
-  return jvalidate::detail::Pointer(std::string_view{data, len});
+  return jvalidate::detail::Pointer::parse(std::string_view{data, len}).value();
 }
 
 inline Json::Value operator""_json(char const * data, size_t len) {