reference.h 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. #pragma once
  2. #include <cstdlib>
  3. #include <ostream>
  4. #include <string>
  5. #include <string_view>
  6. #include <utility>
  7. #include <jvalidate/detail/anchor.h>
  8. #include <jvalidate/detail/expect.h>
  9. #include <jvalidate/detail/out.h>
  10. #include <jvalidate/detail/pointer.h>
  11. #include <jvalidate/uri.h>
  12. namespace jvalidate::detail {
  13. class Reference;
  14. /**
  15. * @brief A class describing a "Reference without a JSON-Pointer" object.
  16. * For the sake of avoiding code-duplication, we implement References in terms
  17. * of this class, which makes this comment awkward.
  18. *
  19. * A RootReference refers to a URI and/or an Anchor object - and is specifically
  20. * meant for use with {@see ReferenceCache} and {@see ReferenceManager} to
  21. * create bindings beween the Anchor/URI points of $id/$anchor tags with their
  22. * absolute reference parents.
  23. *
  24. * Because of this, there is an intrinsic link between RootReference and
  25. * Reference objects, although it is not a 1-1 relationship.
  26. */
  27. class RootReference {
  28. private:
  29. friend class Reference;
  30. URI uri_;
  31. Anchor anchor_;
  32. public:
  33. RootReference() = default;
  34. // Piecewise constructor for RootReference, supports (URI) and (URI, Anchor)
  35. explicit RootReference(URI uri, Anchor anchor = {})
  36. : uri_(std::move(uri)), anchor_(std::move(anchor)) {}
  37. // Parser-ctor for RootReference, implemented in terms of
  38. // {@see RootReference::RootReference(std::string_view, out<size_t>)}, which
  39. // is also used by Reference's parser-ctor.
  40. explicit RootReference(std::string_view ref) : RootReference(ref, discard_out) {}
  41. bool is_relative() const { return uri_.is_relative(); }
  42. URI const & uri() const { return uri_; }
  43. Anchor const & anchor() const { return anchor_; }
  44. friend std::ostream & operator<<(std::ostream & os, RootReference const & self) {
  45. return os << self.uri_ << '#' << self.anchor_;
  46. }
  47. auto operator<=>(RootReference const &) const = default;
  48. private:
  49. /**
  50. * @brief Parse a RootReference out from a given textual representation.
  51. *
  52. * @param ref A string containing a URI and/or anchor. By convention - this
  53. * parameter should be "#" if it is refering to an empty RootReference.
  54. *
  55. * @param[out] end An output variable that tracks the end-position of the
  56. * anchor. When calling {@see Reference::Reference(std::string_view)}, this
  57. * lets us properly offset the view for the JSON-Pointer component without
  58. * needing to re-implement the code that scans for it.
  59. */
  60. RootReference(std::string_view ref, out<size_t> end) {
  61. // By default, RootReference will consume the entire input
  62. end = std::string::npos;
  63. // As also mentioned in URI, the fragment identifier is used in a
  64. // JSON-Reference to separate the URI from the Anchor/Pointer component(s)
  65. size_t const end_of_uri = ref.find('#');
  66. uri_ = URI(ref.substr(0, end_of_uri));
  67. // If there is not a fragment-separator, then this RootReference is all URI
  68. // There will be no Anchor or JSON-Pointer components to extract.
  69. if (end_of_uri == std::string::npos) {
  70. return;
  71. }
  72. // Skip past the "#"
  73. ref.remove_prefix(end_of_uri + 1);
  74. // Anchors prohibit most characters, so we can be sure that the first "/"
  75. // past the URI is the endpoint of the Anchor
  76. size_t const pointer_start = ref.find('/');
  77. anchor_ = Anchor(ref.substr(0, pointer_start));
  78. // Prohibit a trailing JSON-Pointer unless the caller provided the out-param
  79. EXPECT_M(end || pointer_start == std::string::npos, "JSON-Pointer is illegal in this context");
  80. // Ensure proper offset is applied: add pointer_start to end_of_uri because
  81. // we called remove_prefix on ref. Add an additional +1 because of the "#"
  82. if (pointer_start != std::string::npos) {
  83. end = pointer_start + end_of_uri + 1;
  84. }
  85. }
  86. };
  87. /**
  88. * @brief A Reference is a class describing a location of a JSON value. This may
  89. * describe an external document, and anchor within a document (jump to
  90. * location), and/or a path to a value from a parent location. References allow
  91. * us to combine all three of these properties together - although in practice
  92. * the Anchor field should be empty before it escapes the detail namespace.
  93. */
  94. class Reference {
  95. private:
  96. RootReference root_;
  97. Pointer pointer_;
  98. public:
  99. Reference() = default;
  100. // Piecewise constructor for References (URI) and (URI, Pointer)
  101. explicit Reference(URI const & uri, Pointer pointer = {})
  102. : root_(uri), pointer_(std::move(pointer)) {}
  103. // Piecewise constructor for References (URI, Anchor) and (URI, Anchor, Pointer)
  104. explicit Reference(URI const & uri, Anchor const & anchor, Pointer pointer = {})
  105. : root_(uri, anchor), pointer_(std::move(pointer)) {}
  106. // Piecewise constructor for References using RootReference
  107. explicit Reference(RootReference root, Pointer pointer = {})
  108. : root_(std::move(root)), pointer_(std::move(pointer)) {}
  109. explicit Reference(std::string_view ref) {
  110. size_t pointer_start = 0;
  111. root_ = RootReference(ref, pointer_start);
  112. if (pointer_start != std::string::npos) {
  113. pointer_ = Pointer::parse(ref.substr(pointer_start)).value();
  114. }
  115. }
  116. URI const & uri() const { return root_.uri(); }
  117. Anchor const & anchor() const { return root_.anchor(); }
  118. Pointer const & pointer() const { return pointer_; }
  119. RootReference const & root() const { return root_; }
  120. Reference parent() const { return Reference(root_, pointer_.parent()); }
  121. /**
  122. * @brief Delegate function for {@see Pointer::operator/=}, but returning
  123. * this Reference.
  124. */
  125. Reference & operator/=(auto const & next) { return (pointer_ /= next, *this); }
  126. /**
  127. * @brief Delegate function for {@see Pointer::operator/}, but returning
  128. * a Reference.
  129. */
  130. Reference operator/(auto const & next) const { return Reference(*this) /= next; }
  131. friend std::ostream & operator<<(std::ostream & os, Reference const & self) {
  132. return os << self.root_ << self.pointer_;
  133. }
  134. auto operator<=>(Reference const &) const = default;
  135. };
  136. }