pointer.h 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. #pragma once
  2. #include <algorithm>
  3. #include <cassert>
  4. #include <cstdint>
  5. #include <iostream>
  6. #include <string>
  7. #include <string_view>
  8. #include <variant>
  9. #include <vector>
  10. #include <jvalidate/compat/compare.h>
  11. #include <jvalidate/forward.h>
  12. namespace jvalidate::detail {
  13. struct parent_t {};
  14. constexpr parent_t parent;
  15. class Pointer {
  16. public:
  17. Pointer() = default;
  18. Pointer(std::vector<std::variant<std::string, size_t>> const & tokens) : tokens_(tokens) {}
  19. Pointer(std::string_view path) {
  20. if (path.empty()) {
  21. return;
  22. }
  23. auto append_with_parse = [this](std::string in) {
  24. if (not in.empty() && in.find_first_not_of("0123456789") == std::string::npos) {
  25. return tokens_.push_back(std::stoull(in));
  26. }
  27. for (size_t i = 0; i < in.size(); ++i) {
  28. if (in[i] == '%') {
  29. char const enc[3] = {in[i + 1], in[i + 2]};
  30. in.replace(i, 3, 1, char(std::stoi(enc, nullptr, 16)));
  31. } else if (in[i] != '~') {
  32. continue;
  33. }
  34. if (in[i + 1] == '0') {
  35. in.replace(i, 2, 1, '~');
  36. } else if (in[i + 1] == '1') {
  37. in.replace(i, 2, 1, '/');
  38. }
  39. }
  40. tokens_.push_back(std::move(in));
  41. };
  42. path.remove_prefix(1);
  43. // The rules of JSON-Pointer is that if a token were to contain a '/' as a
  44. // strict character: then that character would be escaped, using the above
  45. // rules. We take advantage of string_view's sliding view to make iteration
  46. // easy.
  47. for (size_t p = path.find('/'); p != std::string::npos;
  48. path.remove_prefix(p + 1), p = path.find('/')) {
  49. append_with_parse(std::string(path.substr(0, p)));
  50. }
  51. append_with_parse(std::string(path));
  52. }
  53. auto walk(Adapter auto document) const {
  54. for (auto const & token : tokens_) {
  55. document = std::visit([&document](auto const & next) { return document[next]; }, token);
  56. }
  57. return document;
  58. }
  59. std::string back() const {
  60. struct {
  61. std::string operator()(std::string const & in) const { return in; }
  62. std::string operator()(size_t in) const { return std::to_string(in); }
  63. } g_as_str;
  64. return tokens_.empty() ? "" : std::visit(g_as_str, tokens_.back());
  65. }
  66. bool empty() const { return tokens_.empty(); }
  67. bool starts_with(Pointer const & other) const {
  68. return other.tokens_.size() <= tokens_.size() &&
  69. std::equal(other.tokens_.begin(), other.tokens_.end(), tokens_.begin());
  70. }
  71. Pointer relative_to(Pointer const & other) const {
  72. assert(starts_with(other));
  73. return Pointer(std::vector(tokens_.begin() + other.tokens_.size(), tokens_.end()));
  74. }
  75. Pointer parent() const { return Pointer({tokens_.begin(), tokens_.end() - 1}); }
  76. Pointer & operator/=(Pointer const & relative) {
  77. tokens_.insert(tokens_.end(), relative.tokens_.begin(), relative.tokens_.end());
  78. return *this;
  79. }
  80. Pointer operator/(Pointer const & relative) const { return Pointer(*this) /= relative; }
  81. Pointer & operator/=(parent_t) {
  82. tokens_.pop_back();
  83. return *this;
  84. }
  85. Pointer operator/(parent_t) const { return parent(); }
  86. Pointer & operator/=(std::string_view key) {
  87. tokens_.emplace_back(std::string(key));
  88. return *this;
  89. }
  90. Pointer operator/(std::string_view key) const { return Pointer(*this) /= key; }
  91. Pointer & operator/=(size_t index) {
  92. tokens_.emplace_back(index);
  93. return *this;
  94. }
  95. Pointer operator/(size_t index) const { return Pointer(*this) /= index; }
  96. friend std::ostream & operator<<(std::ostream & os, Pointer const & self) {
  97. for (auto const & elem : self.tokens_) {
  98. std::visit([&os](auto const & v) { os << '/' << v; }, elem);
  99. }
  100. return os;
  101. }
  102. auto operator<=>(Pointer const &) const = default;
  103. private:
  104. std::vector<std::variant<std::string, size_t>> tokens_{};
  105. };
  106. }