#![allow(dead_code)]
use std::{borrow::Cow, collections::VecDeque, fmt::Display, ops::Add};
const PATH_SEPARATOR: char = '/';
const ENCODED_TILDE: &str = "~0";
const ENCODED_SLASH: &str = "~1";
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum JsonPointerComponent<'a> {
Root,
Name(Cow<'a, str>),
Index(usize),
}
impl<'a> Display for JsonPointerComponent<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Root => write!(f, ""),
Self::Name(s) => write!(
f,
"{}",
&s.replace("~", ENCODED_TILDE).replace("/", ENCODED_SLASH)
),
Self::Index(i) => write!(f, "{}", i),
}
}
}
#[derive(Debug, Default, Hash, Eq)]
pub struct JsonPointer<'a> {
components: VecDeque<JsonPointerComponent<'a>>,
}
impl<'a> PartialEq for JsonPointer<'a> {
fn eq(&self, other: &Self) -> bool {
self.matches(other)
}
}
impl<'a> JsonPointer<'a> {
pub fn root() -> Self {
let mut ptr = JsonPointer::default();
ptr.components.push_back(JsonPointerComponent::Root);
ptr
}
pub fn is_root(&self) -> bool {
return !self.components.is_empty()
&& self.components.len() == 1
&& self.components[0] == JsonPointerComponent::Root;
}
pub fn len(&self) -> usize {
self.components.len()
}
pub fn is_empty(&self) -> bool {
self.components.is_empty()
}
pub fn push_names(&mut self, names: &[&'a str]) {
names.iter().for_each(|n| self.push_name(n.to_string()))
}
pub fn push_indexes(&mut self, indexes: &[usize]) {
indexes.iter().for_each(|i| self.push_index(*i))
}
pub fn push_name(&mut self, name: String) {
if self.is_empty() {
self.components.push_back(JsonPointerComponent::Root)
}
self.components
.push_back(JsonPointerComponent::Name(Cow::Owned(name)))
}
pub fn push_index(&mut self, index: usize) {
if self.is_empty() {
self.components.push_back(JsonPointerComponent::Root)
}
self.components
.push_back(JsonPointerComponent::Index(index))
}
pub fn pop(&mut self) -> Option<JsonPointerComponent<'a>> {
self.components.pop_back()
}
pub fn matches(&self, rhs: &'a JsonPointer) -> bool {
self.as_str() == rhs.as_str()
}
pub fn as_str(&self) -> Cow<'a, str> {
if self.is_root() {
return Cow::Owned("/".to_string());
}
if self.is_empty() {
return Cow::Owned("".to_string());
}
Cow::Owned(
self.components
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join("/"),
)
}
}
impl<'a> Display for JsonPointer<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl<'a> Add<&JsonPointer<'a>> for JsonPointer<'a> {
type Output = Self;
fn add(mut self, rhs: &JsonPointer<'a>) -> Self {
rhs.components
.iter()
.for_each(|c| self.components.push_back(c.clone()));
self
}
}
#[cfg(test)]
mod tests {
use super::JsonPointer;
#[test]
fn an_empty_pointer_should_be_represented_by_an_empty_string() {
let s = JsonPointer::default().as_str();
assert_eq!(s, "")
}
#[test]
fn a_root_pointer_should_be_represented_by_a_single_slash() {
let s = JsonPointer::root().as_str();
assert_eq!(s, "/")
}
#[test]
fn pointers_should_serialise_correctly() {
let mut s = JsonPointer::default();
s.push_names(&vec!["a", "b"]);
assert_eq!("/a/b", s.as_str())
}
#[test]
fn pointers_should_serialise_with_escapes_correctly() {
let mut s = JsonPointer::default();
s.push_names(&vec!["a/b", "c~d"]);
s.push_index(3);
assert_eq!("/a~1b/c~0d/3", s.as_str())
}
#[test]
fn popping_should_shorten_pointers_correctly() {
let mut s = JsonPointer::default();
s.push_names(&vec!["a", "b", "c"]);
assert_eq!("/a/b/c", s.as_str());
s.pop();
assert_eq!("/a/b", s.as_str())
}
#[test]
fn popping_all_components_should_result_in_a_root_pointer() {
let mut s = JsonPointer::default();
s.push_names(&vec!["a", "b", "c"]);
s.pop();
s.pop();
s.pop();
assert_eq!("/", s.as_str())
}
#[test]
fn pointers_should_serialise_indices_correctly() {
let mut s = JsonPointer::default();
s.push_index(0);
s.push_index(3);
s.push_index(2);
assert_eq!("/0/3/2", s.as_str())
}
#[test]
fn pointers_should_match() {
let mut s = JsonPointer::default();
let mut t = JsonPointer::default();
s.push_name("b".to_string());
s.push_index(9);
t.push_name("b".to_string());
t.push_index(9);
assert!(s.matches(&t))
}
#[test]
fn pointers_should_match_using_equality_ops() {
let mut s = JsonPointer::default();
let mut t = JsonPointer::default();
let mut u = JsonPointer::default();
s.push_name("b".to_string());
s.push_index(9);
t.push_name("b".to_string());
t.push_index(9);
u.push_name("x".to_string());
assert_eq!(s, t);
assert_ne!(t, u);
assert_ne!(s, u)
}
}