#pragma once #include #include #include #include #include #include #include #include #include #include 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: static expected parse(std::string_view path) { if (path == "0") { // A literal RelativePointer of "0" simply means "here" return RelativePointer(); } RelativePointer rval; if (size_t const pos = path.find('/'); pos != std::string_view::npos) { // Handle the JSON-Pointer version expected ptr = Pointer::parse(path.substr(pos)); JVALIDATE_PROPIGATE_UNEXPECTED(ptr); rval.pointer_ = *std::move(ptr); path.remove_suffix(path.size() - pos); } else if (path.ends_with('#')) { rval.requests_key_ = true; path.remove_suffix(1); } if (path.starts_with('0') && path != "0") { return unexpected("Cannot zero-prefix a relative pointer"); } size_t read = 0; expected parent_steps = parse_integer(path, {.read = read}); // Will return unexpected if path contains any non-numeric characters, or // if path represents a number that is out of the bounds of a size_t. JVALIDATE_PROPIGATE_UNEXPECTED(parent_steps.transform_error(to_message)); rval.parent_steps_ = *parent_steps; return rval; } /** * @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 std::variant 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_; }; }