reference.h 5.7 KB

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