use jsonptr::{Pointer, PointerBuf};
use std::cmp::Ordering;
use std::fmt::Display;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
#[derive(Clone, Default, PartialEq, Eq, Debug)]
pub struct Path(PointerBuf);
impl PartialOrd for Path {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Path {
fn cmp(&self, other: &Self) -> Ordering {
self.0
.count()
.cmp(&other.0.count())
.then(self.0.cmp(&other.0))
}
}
impl Path {
pub(crate) fn new(pointer: &Pointer) -> Self {
Self(pointer.to_buf())
}
pub fn from_static(s: &'static str) -> Self {
Path(Pointer::from_static(s).to_buf())
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
pub fn is_prefix_of(&self, other: &Path) -> bool {
let self_tokens: Vec<_> = self.0.tokens().collect();
let other_tokens: Vec<_> = other.0.tokens().collect();
if self_tokens.is_empty() {
return true;
}
if self_tokens.len() > other_tokens.len() {
return false;
}
self_tokens
.iter()
.zip(other_tokens.iter())
.all(|(a, b)| a == b)
}
}
impl Display for Path {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<Path> for String {
fn from(path: Path) -> String {
path.0.to_string()
}
}
impl From<Path> for PointerBuf {
fn from(path: Path) -> Self {
path.0
}
}
impl AsRef<Pointer> for Path {
fn as_ref(&self) -> &Pointer {
&self.0
}
}
impl Deref for Path {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub(crate) struct PathArgs(Vec<(Arc<str>, String)>);
impl PathArgs {
pub fn iter(&self) -> impl Iterator<Item = &(Arc<str>, String)> {
self.0.iter()
}
pub fn insert(&mut self, key: impl AsRef<str>, value: impl Into<String>) -> Option<String> {
let existing = self.0.iter_mut().find(|(k, _)| k.as_ref() == key.as_ref());
if let Some((_, v)) = existing {
let old = v.clone();
*v = value.into();
return Some(old);
} else {
self.0.push((Arc::from(key.as_ref()), value.into()))
}
None
}
pub fn contains_key(&self, key: impl AsRef<str>) -> bool {
self.0.iter().any(|(k, _)| k.as_ref() == key.as_ref())
}
}
impl Display for PathArgs {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let entries = self
.0
.iter()
.map(|(key, value)| format!("\"{key}\": \"{value}\""))
.collect::<Vec<_>>()
.join(", ");
write!(f, "{{{entries}}}")
}
}
impl Deref for PathArgs {
type Target = Vec<(Arc<str>, String)>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for PathArgs {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<matchit::Params<'_, '_>> for PathArgs {
fn from(params: matchit::Params) -> PathArgs {
let params: Vec<(Arc<str>, String)> = params
.iter()
.map(|(k, v)| (Arc::from(k), String::from(v)))
.collect();
PathArgs(params)
}
}
impl<K: AsRef<str>, V: Into<String>> From<Vec<(K, V)>> for PathArgs {
fn from(args: Vec<(K, V)>) -> Self {
PathArgs(
args.into_iter()
.map(|(k, v)| (Arc::from(k.as_ref()), v.into()))
.collect(),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_sorts_path_by_length_and_then_lexicographically() {
let mut paths = vec![
Path::from_static("/a/b/c"),
Path::from_static(""),
Path::from_static("/a/b"),
Path::from_static("/"),
Path::from_static("/a/f"),
Path::from_static("/x"),
Path::from_static("/a/b/d"),
];
paths.sort();
assert_eq!(
paths,
vec![
Path::from_static(""),
Path::from_static("/"),
Path::from_static("/x"),
Path::from_static("/a/b"),
Path::from_static("/a/f"),
Path::from_static("/a/b/c"),
Path::from_static("/a/b/d"),
]
)
}
#[test]
fn it_converts_a_path_to_string() {
assert_eq!(Path::from_static("/a/b/c").to_string(), "/a/b/c");
assert_eq!(Path::from_static("/").to_string(), "/");
assert_eq!(Path::from_static("").to_string(), "");
}
#[test]
fn it_converts_a_str_to_a_path() {
let path = Path::from_static("/a/b/c");
assert_eq!(String::from(path), "/a/b/c");
}
#[test]
#[should_panic]
fn it_panics_on_an_invalid_path() {
Path::from_static("a/b/c");
}
#[test]
fn it_replaces_existing_key_with_insert() {
let mut args = PathArgs::default();
args.insert("one", "a");
args.insert("two", "b");
assert_eq!(
args.0,
vec![
(Arc::from("one"), "a".to_string()),
(Arc::from("two"), "b".to_string())
]
);
let old = args.insert("one", "c");
assert_eq!(
args.0,
vec![
(Arc::from("one"), "c".to_string()),
(Arc::from("two"), "b".to_string())
]
);
assert_eq!(old, Some("a".to_string()))
}
#[test]
fn test_is_prefix_of() {
let path_a = Path::from_static("/a");
let path_a_b = Path::from_static("/a/b");
let path_a_b_c = Path::from_static("/a/b/c");
let path_aa = Path::from_static("/aa");
let path_b = Path::from_static("/b");
let root = Path::from_static("");
assert!(path_a.is_prefix_of(&path_a_b));
assert!(path_a.is_prefix_of(&path_a_b_c));
assert!(path_a_b.is_prefix_of(&path_a_b_c));
assert!(root.is_prefix_of(&path_a));
assert!(root.is_prefix_of(&path_a_b));
assert!(path_a.is_prefix_of(&path_a));
assert!(!path_a.is_prefix_of(&path_aa)); assert!(!path_a.is_prefix_of(&path_b));
assert!(!path_a_b.is_prefix_of(&path_a)); assert!(!path_aa.is_prefix_of(&path_a));
}
}