| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 |
- #pragma once
- #include <cstdlib>
- #include <ostream>
- #include <string>
- #include <string_view>
- #include <variant>
- #include <jvalidate/detail/expect.h>
- #include <jvalidate/detail/number.h>
- #include <jvalidate/detail/pointer.h>
- #include <jvalidate/forward.h>
- namespace jvalidate::detail {
- /**
- * @brief A special form of json pointer that is allowed to specify moving up
- * into the parent scope of the current location.
- * A relative pointer has two forms:
- * - Nth parent-key, which takes the ABNF form:
- * non-negative-integer "#"
- * - Nth parent followed by a JSON-Pointer as specified by RFC6901
- *
- * This does not comply with array neighbor offsets as described in
- * Section 4 Paragraph 3 (the "index-manipulation" ABNF).
- *
- * https://json-schema.org/draft/2020-12/relative-json-pointer
- * https://datatracker.ietf.org/doc/html/rfc6901
- */
- class RelativePointer {
- public:
- explicit(false) RelativePointer(std::string_view path) {
- if (path == "0") { // A literal RelativePointer of "0" simply means "here"
- return;
- }
- if (size_t const pos = path.find('/'); pos != std::string_view::npos) {
- // Handle the JSON-Pointer version
- pointer_ = Pointer(path.substr(pos));
- path.remove_suffix(path.size() - pos);
- } else if (path.ends_with('#')) {
- requests_key_ = true;
- path.remove_suffix(1);
- }
- EXPECT_M(path == "0" || not path.starts_with("0"), "Cannot zero-prefix a relative pointer");
- // Will throw an exception if path contains any non-numeric characters, or
- // if path represents a number that is out of the bounds of a size_t.
- parent_steps_ = from_str<size_t>(path);
- }
- /**
- * @brief Acquire either the key of the nth parent or a JSON value at some "cousin"
- * location in the overall document.
- *
- * @tparam A JSON Adapter type
- *
- * @param where The pointer representing the current location.
- * @param root The root document location (accessed by where == Pointer())
- *
- * @return The evaluation result as described by
- * https://json-schema.org/draft/2020-12/relative-json-pointer#rfc.section.4
- */
- template <Adapter A>
- std::variant<std::string, A> inspect(Pointer const & where, A const & root) const {
- if (requests_key_) {
- return where.parent(parent_steps_).back();
- }
- auto rval = where.parent(parent_steps_).walk(root);
- return pointer_.walk(rval);
- }
- friend std::ostream & operator<<(std::ostream & os, RelativePointer const & rel) {
- os << rel.parent_steps_;
- if (rel.requests_key_) {
- return os << '#';
- }
- os << rel.pointer_;
- return os;
- }
- private:
- size_t parent_steps_ = 0;
- bool requests_key_ = false;
- Pointer pointer_;
- };
- }
|