relative_pointer.h 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. #pragma once
  2. #include <ostream>
  3. #include <string>
  4. #include <string_view>
  5. #include <jvalidate/detail/expect.h>
  6. #include <jvalidate/detail/number.h>
  7. #include <jvalidate/detail/pointer.h>
  8. #include <jvalidate/forward.h>
  9. namespace jvalidate::detail {
  10. /**
  11. * @brief A special form of json pointer that is allowed to specify moving up
  12. * into the parent scope of the current location.
  13. * A relative pointer has two forms:
  14. * - Nth parent-key, which takes the ABNF form:
  15. * non-negative-integer "#"
  16. * - Nth parent followed by a JSON-Pointer as specified by RFC6901
  17. *
  18. * This does not comply with array neighbor offsets as described in
  19. * Section 4 Paragraph 3 (the "index-manipulation" ABNF).
  20. *
  21. * https://json-schema.org/draft/2020-12/relative-json-pointer
  22. * https://datatracker.ietf.org/doc/html/rfc6901
  23. */
  24. class RelativePointer {
  25. public:
  26. RelativePointer(std::string_view path) {
  27. if (path == "0") { // A literal RelativePointer of "0" simply means "here"
  28. return;
  29. }
  30. if (size_t pos = path.find('/'); pos != path.npos) {
  31. // Handle the JSON-Pointer version
  32. pointer_ = Pointer(path.substr(pos));
  33. path.remove_suffix(path.size() - pos);
  34. } else if (path.ends_with('#')) {
  35. requests_key_ = true;
  36. path.remove_suffix(1);
  37. }
  38. EXPECT_M(path == "0" || not path.starts_with("0"), "Cannot zero-prefix a relative pointer");
  39. // Will throw an exception if path contains any non-numeric characters, or
  40. // if path represents a number that is out of the bounds of a size_t.
  41. parent_steps_ = from_str<size_t>(path);
  42. }
  43. /**
  44. * @brief Acquire either the key of the nth parent or a JSON value at some "cousin"
  45. * location in the overall document.
  46. *
  47. * @tparam A JSON Adapter type
  48. *
  49. * @param where The pointer representing the current location.
  50. * @param root The root document location (accessed by where == Pointer())
  51. *
  52. * @return The evaluation result as described by
  53. * https://json-schema.org/draft/2020-12/relative-json-pointer#rfc.section.4
  54. */
  55. template <Adapter A>
  56. std::variant<std::string, A> inspect(Pointer const & where, A const & root) const {
  57. if (requests_key_) {
  58. return where.parent(parent_steps_).back();
  59. }
  60. auto rval = where.parent(parent_steps_).walk(root);
  61. return pointer_.walk(rval);
  62. }
  63. friend std::ostream & operator<<(std::ostream & os, RelativePointer const & rel) {
  64. os << rel.parent_steps_;
  65. if (rel.requests_key_) {
  66. return os << '#';
  67. }
  68. os << rel.pointer_;
  69. return os;
  70. }
  71. private:
  72. size_t parent_steps_ = 0;
  73. bool requests_key_ = false;
  74. Pointer pointer_ = {};
  75. };
  76. }