| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- #pragma once
- #include <algorithm>
- #include <cassert>
- #include <cstdint>
- #include <iostream>
- #include <string>
- #include <string_view>
- #include <variant>
- #include <vector>
- #include <jvalidate/detail/compare.h>
- #include <jvalidate/forward.h>
- namespace jvalidate::detail {
- struct parent_t {};
- constexpr parent_t parent;
- class Pointer {
- public:
- Pointer() = default;
- Pointer(std::vector<std::variant<std::string, size_t>> const & tokens) : tokens_(tokens) {}
- Pointer(std::string_view path) {
- if (path.empty()) {
- return;
- }
- auto append_with_parse = [this](std::string in) {
- if (not in.empty() && in.find_first_not_of("0123456789") == std::string::npos) {
- return tokens_.push_back(std::stoull(in));
- }
- for (size_t i = 0; i < in.size(); ++i) {
- if (in[i] == '%') {
- char const enc[3] = {in[i + 1], in[i + 2]};
- in.replace(i, 3, 1, char(std::stoi(enc, nullptr, 16)));
- } else if (in[i] != '~') {
- continue;
- }
- if (in[i + 1] == '0') {
- in.replace(i, 2, 1, '~');
- } else if (in[i + 1] == '1') {
- in.replace(i, 2, 1, '/');
- }
- }
- tokens_.push_back(std::move(in));
- };
- path.remove_prefix(1);
- size_t p = path.find('/');
- for (; p != std::string::npos; path.remove_prefix(p + 1), p = path.find('/')) {
- append_with_parse(std::string(path.substr(0, p)));
- }
- append_with_parse(std::string(path));
- }
- template <Adapter A> A walk(A document) const {
- for (auto const & token : tokens_) {
- document = std::visit([&document](auto const & next) { return document[next]; }, token);
- }
- return document;
- }
- bool empty() const { return tokens_.empty(); }
- bool starts_with(Pointer const & other) const {
- return other.tokens_.size() <= tokens_.size() &&
- std::equal(other.tokens_.begin(), other.tokens_.end(), tokens_.begin());
- }
- Pointer relative_to(Pointer const & other) const {
- assert(starts_with(other));
- return Pointer(std::vector(tokens_.begin() + other.tokens_.size(), tokens_.end()));
- }
- Pointer parent() const { return Pointer({tokens_.begin(), tokens_.end() - 1}); }
- Pointer & operator/=(Pointer const & relative) {
- tokens_.insert(tokens_.end(), relative.tokens_.begin(), relative.tokens_.end());
- return *this;
- }
- Pointer operator/(Pointer const & relative) const { return Pointer(*this) /= relative; }
- Pointer & operator/=(parent_t) {
- tokens_.pop_back();
- return *this;
- }
- Pointer operator/(parent_t) const { return parent(); }
- Pointer & operator/=(std::string_view key) {
- tokens_.emplace_back(std::string(key));
- return *this;
- }
- Pointer operator/(std::string_view key) const { return Pointer(*this) /= key; }
- Pointer & operator/=(size_t index) {
- tokens_.emplace_back(index);
- return *this;
- }
- Pointer operator/(size_t index) const { return Pointer(*this) /= index; }
- friend std::ostream & operator<<(std::ostream & os, Pointer const & self) {
- for (auto const & elem : self.tokens_) {
- std::visit([&os](auto const & v) { os << '/' << v; }, elem);
- }
- return os;
- }
- auto operator<=>(Pointer const &) const = default;
- private:
- std::vector<std::variant<std::string, size_t>> tokens_{};
- };
- }
|