| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- #pragma once
- #include <string>
- #include <string_view>
- #include <jvalidate/detail/anchor.h>
- #include <jvalidate/detail/expect.h>
- #include <jvalidate/detail/out.h>
- #include <jvalidate/detail/pointer.h>
- #include <jvalidate/uri.h>
- namespace jvalidate::detail {
- class Reference;
- /**
- * @brief A class describing a "Reference without a JSON-Pointer" object.
- * For the sake of avoiding code-duplication, we implement References in terms
- * of this class, which makes this comment awkward.
- *
- * A RootReference refers to a URI and/or an Anchor object - and is specifically
- * meant for use with {@see ReferenceCache} and {@see ReferenceManager} to
- * create bindings beween the Anchor/URI points of $id/$anchor tags with their
- * absolute reference parents.
- *
- * Because of this, there is an intrinsic link between RootReference and
- * Reference objects, although it is not a 1-1 relationship.
- */
- class RootReference {
- private:
- friend class Reference;
- URI uri_;
- Anchor anchor_;
- public:
- RootReference() = default;
- // Piecewise constructor for RootReference, supports (URI) and (URI, Anchor)
- explicit RootReference(URI const & uri, Anchor const & anchor = {})
- : uri_(uri), anchor_(anchor) {}
- // Parser-ctor for RootReference, implemented in terms of
- // {@see RootReference::RootReference(std::string_view, out<size_t>)}, which
- // is also used by Reference's parser-ctor.
- explicit RootReference(std::string_view ref) : RootReference(ref, discard_out) {}
- bool is_relative() const { return uri_.is_relative(); }
- URI const & uri() const { return uri_; }
- Anchor const & anchor() const { return anchor_; }
- friend std::ostream & operator<<(std::ostream & os, RootReference const & self) {
- return os << self.uri_ << '#' << self.anchor_;
- }
- auto operator<=>(RootReference const &) const = default;
- private:
- /**
- * @brief Parse a RootReference out from a given textual representation.
- *
- * @param ref A string containing a URI and/or anchor. By convention - this
- * parameter should be "#" if it is refering to an empty RootReference.
- *
- * @param[out] end An output variable that tracks the end-position of the
- * anchor. When calling {@see Reference::Reference(std::string_view)}, this
- * lets us properly offset the view for the JSON-Pointer component without
- * needing to re-implement the code that scans for it.
- */
- RootReference(std::string_view ref, out<size_t> end) {
- // By default, RootReference will consume the entire input
- end = std::string::npos;
- // As also mentioned in URI, the fragment identifier is used in a
- // JSON-Reference to separate the URI from the Anchor/Pointer component(s)
- size_t end_of_uri = ref.find('#');
- uri_ = URI(ref.substr(0, end_of_uri));
- // If there is not a fragment-separator, then this RootReference is all URI
- // There will be no Anchor or JSON-Pointer components to extract.
- if (end_of_uri == std::string::npos) {
- return;
- }
- // Skip past the "#"
- ref.remove_prefix(end_of_uri + 1);
- // Anchors prohibit most characters, so we can be sure that the first "/"
- // past the URI is the endpoint of the Anchor
- size_t const pointer_start = ref.find('/');
- anchor_ = Anchor(ref.substr(0, pointer_start));
- // Prohibit a trailing JSON-Pointer unless the caller provided the out-param
- EXPECT_M(end || pointer_start == std::string::npos, "JSON-Pointer is illegal in this context");
- // Ensure proper offset is applied: add pointer_start to end_of_uri because
- // we called remove_prefix on ref. Add an additional +1 because of the "#"
- if (pointer_start != std::string::npos) {
- end = pointer_start + end_of_uri + 1;
- }
- }
- };
- /**
- * @brief A Reference is a class describing a location of a JSON value. This may
- * describe an external document, and anchor within a document (jump to
- * location), and/or a path to a value from a parent location. References allow
- * us to combine all three of these properties together - although in practice
- * the Anchor field should be empty before it escapes the detail namespace.
- */
- class Reference {
- private:
- RootReference root_;
- Pointer pointer_;
- public:
- Reference() = default;
- // Piecewise constructor for References (URI) and (URI, Pointer)
- explicit Reference(URI const & uri, Pointer const & pointer = {})
- : root_(uri), pointer_(pointer) {}
- // Piecewise constructor for References (URI, Anchor) and (URI, Anchor, Pointer)
- explicit Reference(URI const & uri, Anchor const & anchor, Pointer const & pointer = {})
- : root_(uri, anchor), pointer_(pointer) {}
- // Piecewise constructor for References using RootReference
- explicit Reference(RootReference const & root, Pointer const & pointer = {})
- : root_(root), pointer_(pointer) {}
- explicit Reference(std::string_view ref) {
- size_t pointer_start = 0;
- root_ = RootReference(ref, pointer_start);
- if (pointer_start != std::string::npos) {
- pointer_ = ref.substr(pointer_start);
- }
- }
- URI const & uri() const { return root_.uri(); }
- Anchor const & anchor() const { return root_.anchor(); }
- Pointer const & pointer() const { return pointer_; }
- RootReference const & root() const { return root_; }
- Reference parent() const { return Reference(root_, pointer_.parent()); }
- /**
- * @brief Delegate function for {@see Pointer::operator/=}, but returning
- * this Reference.
- */
- Reference & operator/=(auto const & in) { return (pointer_ /= in, *this); }
- /**
- * @brief Delegate function for {@see Pointer::operator/}, but returning
- * a Reference.
- */
- Reference operator/(auto const & in) const { return Reference(*this) /= in; }
- friend std::ostream & operator<<(std::ostream & os, Reference const & self) {
- return os << self.root_ << self.pointer_;
- }
- auto operator<=>(Reference const &) const = default;
- };
- }
|