#pragma once #include #include #include #include #include #include #include #include #include #include namespace jvalidate::detail { struct parent_t {}; constexpr parent_t parent; class Pointer { public: Pointer() = default; Pointer(std::vector> 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 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> tokens_{}; }; }