use core::{
borrow::Borrow,
cmp::Ordering,
mem,
ops::{ControlFlow, Deref},
str::{FromStr, Split},
};
use crate::{
Assignment, Error, MalformedPointerError, NotFoundError, OutOfBoundsError, ReplaceTokenError,
Token, Tokens, UnresolvableError,
};
use alloc::{
borrow::{Cow, ToOwned},
string::{String, ToString},
vec,
vec::Vec,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MaybePointer {
Pointer(Pointer),
Malformed(String, MalformedPointerError),
}
impl Deref for MaybePointer {
type Target = str;
fn deref(&self) -> &Self::Target {
match self {
MaybePointer::Pointer(p) => p.as_str(),
MaybePointer::Malformed(s, _) => s,
}
}
}
impl<'de> Deserialize<'de> for MaybePointer {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
String::deserialize(deserializer).map(|s| match Pointer::from_str(&s) {
Ok(ptr) => MaybePointer::Pointer(ptr),
Err(e) => MaybePointer::Malformed(s, e),
})
}
}
impl Serialize for MaybePointer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
MaybePointer::Pointer(ptr) => serializer.serialize_str(ptr.as_str()),
MaybePointer::Malformed(s, _) => serializer.serialize_str(s),
}
}
}
#[cfg_attr(
feature = "url",
doc = r##"
```rust
use jsonptr::Pointer;
let expected = Pointer::new(&["foo", "bar"]);
let url = url::Url::parse("https://example.com#/foo/bar").unwrap();
assert_eq!(expected, Pointer::try_from(url).unwrap())
```
"##
)]
#[derive(Clone, Default)]
pub struct Pointer {
inner: String,
count: usize,
}
impl Pointer {
pub fn root() -> Self {
Self::default()
}
pub fn new<V, T>(tokens: V) -> Self
where
V: AsRef<[T]>,
Token: for<'a> From<&'a T>,
{
let mut inner = String::new();
let tokens = tokens.as_ref();
for t in tokens.iter().map(Into::<Token>::into) {
inner.push('/');
inner.push_str(t.encoded());
}
Pointer {
inner,
count: tokens.len(),
}
}
pub fn parse(value: &str) -> Result<Self, MalformedPointerError> {
Self::from_str(value)
}
pub fn as_str(&self) -> &str {
&self.inner
}
pub fn push_front(&mut self, token: Token) {
self.count += 1;
self.inner.insert(0, '/');
self.inner.insert_str(1, token.encoded());
}
pub fn push_back(&mut self, token: Token) {
self.count += 1;
self.inner.push('/');
self.inner.push_str(token.encoded());
}
pub fn pop_back(&mut self) -> Option<Token> {
if self.is_root() {
return None;
}
if self.count > 0 {
self.count -= 1;
}
let (front, back) = self.inner.rsplit_once('/').expect("`self.count` was > 0");
let back = Token::from_encoded(back);
self.inner = front.to_owned();
Some(back)
}
pub fn pop_front(&mut self) -> Option<Token> {
(!self.inner.is_empty() && self.count > 0).then(|| {
self.count -= 1;
if let Some((front, back)) = self.inner[1..].split_once('/') {
let front = Token::from_encoded(front);
self.inner = String::from("/") + back;
front
} else {
let token = Token::from_encoded(&self.inner[1..]);
self.inner.truncate(0);
token
}
})
}
pub fn count(&self) -> usize {
self.count
}
pub fn is_root(&self) -> bool {
self.inner.is_empty()
}
pub fn to_value(&self) -> Value {
Value::String(self.to_string())
}
pub fn back(&self) -> Option<Token> {
if self.is_root() {
return None;
}
let (_, back) = self
.inner
.rsplit_once('/')
.expect("`self.is_root()` is false, thus pointer starts with `/`");
Some(Token::from_encoded(back))
}
pub fn last(&self) -> Option<Token> {
self.back()
}
pub fn front(&self) -> Option<Token> {
if self.is_root() {
return None;
}
self.inner[1..]
.split_once('/')
.map_or_else(
|| Token::from_encoded(&self.inner[1..]),
|(front, _)| Token::from_encoded(front),
)
.into()
}
pub fn first(&self) -> Option<Token> {
self.front()
}
pub fn append(&mut self, other: &Pointer) -> &Pointer {
self.count += other.count;
if self.is_root() {
self.inner = other.inner.clone();
} else if !other.is_root() {
self.inner.push_str(&other.inner);
}
self
}
pub fn get(&self, index: usize) -> Option<Token> {
if self.is_root() {
return None;
}
let tokens = self.tokens().collect::<Vec<_>>();
tokens.get(index).cloned()
}
pub fn replace_token(
&mut self,
index: usize,
token: Token,
) -> Result<Option<Token>, ReplaceTokenError> {
if self.is_root() {
return Err(ReplaceTokenError {
count: self.count,
index,
pointer: self.clone(),
});
}
let mut tokens = self.tokens().collect::<Vec<_>>();
if index > tokens.len() {
return Err(ReplaceTokenError {
count: tokens.len(),
index,
pointer: self.clone(),
});
}
let old = tokens.get(index).cloned();
tokens[index] = token;
self.inner = String::from("/")
+ &tokens
.iter()
.map(Token::encoded)
.collect::<Vec<_>>()
.join("/");
Ok(old)
}
pub fn clear(&mut self) {
self.count = 0;
self.inner = String::from("");
}
pub fn tokens(&self) -> Tokens {
Tokens::new(self.split())
}
pub fn resolve<'v>(&self, value: &'v Value) -> Result<&'v Value, Error> {
let Resolved {
value,
remaining,
mut resolved,
} = self.try_resolve(value)?;
if remaining.is_root() {
Ok(value)
} else {
match value {
Value::Object(_) => {
if !remaining.is_root() {
resolved.push_back(remaining.front().unwrap());
}
Err(NotFoundError::new(resolved).into())
}
Value::Array(_) => {
if !remaining.is_root() {
resolved.push_back(remaining.front().unwrap());
}
Err(NotFoundError::new(resolved).into())
}
Value::Null => {
if !remaining.is_root() {
resolved.push_back(remaining.front().unwrap());
}
Err(NotFoundError::new(resolved).into())
}
_ => {
if let Some(t) = remaining.front() {
resolved.push_back(t);
}
Err(UnresolvableError::new(resolved).into())
}
}
}
}
pub fn resolve_mut<'v>(&self, value: &'v mut Value) -> Result<&'v mut Value, Error> {
let ResolvedMut {
value,
remaining,
mut resolved,
} = self.try_resolve_mut(value)?;
if remaining.is_root() {
Ok(value)
} else {
match value {
Value::Object(_) => {
if !remaining.is_root() {
resolved.push_back(remaining.front().unwrap());
}
Err(NotFoundError::new(resolved).into())
}
Value::Array(_) => {
if !remaining.is_root() {
resolved.push_back(remaining.front().unwrap());
}
Err(NotFoundError::new(resolved).into())
}
Value::Null => {
if !remaining.is_root() {
resolved.push_back(remaining.front().unwrap());
}
Err(NotFoundError::new(resolved).into())
}
_ => {
if let Some(t) = remaining.front() {
resolved.push_back(t);
}
Err(UnresolvableError::new(resolved).into())
}
}
}
}
pub fn union(&self, other: &Pointer) -> Pointer {
let mut res = Pointer::default();
for (i, token) in self.tokens().enumerate() {
if let Some(t) = other.get(i) {
if t == token {
res.push_back(t);
} else {
break;
}
} else {
break;
}
}
res
}
pub fn delete(&self, value: &mut Value) -> Option<Value> {
if self.is_root() {
return Some(mem::replace(value, Value::Null));
}
let mut ptr = self.clone();
let last_token = ptr.pop_back().unwrap();
match ptr.try_resolve_mut(value) {
Ok(ResolvedMut {
remaining,
resolved: _resolved,
value,
}) => {
if remaining.is_root() {
match value {
Value::Array(arr) => match last_token.as_index(arr.len()) {
Ok(idx) => Some(arr.remove(idx)),
Err(_) => None,
},
Value::Object(obj) => obj.remove(last_token.as_key()),
_ => None,
}
} else {
None
}
}
Err(_) => None,
}
}
pub fn assign<'d, V>(&self, dest: &'d mut Value, src: V) -> Result<Assignment<'d>, Error>
where
V: Into<Value>,
{
match self.try_resolve_mut(dest) {
Ok(ResolvedMut {
value: dest,
mut remaining,
mut resolved,
}) => {
if remaining.is_root() {
let replaced = mem::replace(dest, src.into());
Ok(Assignment {
replaced,
assigned: Cow::Borrowed(dest),
created_or_mutated: self.clone(),
assigned_to: self.clone(),
})
} else {
let Assigned {
assigned,
replaced,
to,
} = remaining.assign_value(dest, src.into())?;
resolved.append(&to);
Ok(Assignment {
replaced,
assigned: Cow::Borrowed(assigned),
created_or_mutated: remaining,
assigned_to: resolved,
})
}
}
Err(err) => Err(err),
}
}
fn assign_value<'a, V>(&mut self, dest: &'a mut Value, src: V) -> Result<Assigned<'a>, Error>
where
V: Into<Value>,
{
if self.is_root() {
let replaced = mem::replace(dest, src.into());
return Ok(Assigned {
assigned: dest,
replaced,
to: Pointer::default(),
});
}
let mut replaced = Value::Null;
let token = self.pop_front().unwrap();
if dest.is_boolean() || dest.is_null() || dest.is_string() || dest.is_number() {
let new_dest = if token.as_index(0).is_ok() {
Value::Array(vec![])
} else {
Value::Object(serde_json::Map::new())
};
replaced = mem::replace(dest, new_dest);
}
let mut idx: Option<usize> = None;
if dest.is_array() {
let len = dest.as_array().unwrap().len();
let i = token.as_index(len)?;
idx = Some(i);
if self.is_root() {
if i == len {
dest.as_array_mut().unwrap().push(src.into());
} else {
replaced = dest.as_array_mut().unwrap().get(i).cloned().unwrap();
dest.as_array_mut().unwrap()[i] = src.into();
}
let assigned = dest.as_array_mut().unwrap().get_mut(i).unwrap();
return Ok(Assigned {
assigned,
replaced,
to: i.into(),
});
} else if let Some(repl) = self.create_next(dest, &token, Some(i))? {
replaced = repl;
}
} else if self.is_root() {
if let Some(old_val) = dest
.as_object_mut()
.unwrap()
.insert(token.to_string(), src.into())
{
replaced = old_val;
}
let assigned = dest
.as_object_mut()
.unwrap()
.get_mut(token.as_key())
.unwrap();
return Ok(Assigned {
assigned,
replaced,
to: token.into(),
});
} else if let Some(repl) = self.create_next(dest, &token, None)? {
replaced = repl;
}
let assigned = if dest.is_array() {
let idx = idx.unwrap();
dest.as_array_mut().unwrap().get_mut(idx).unwrap()
} else {
dest.as_object_mut()
.unwrap()
.get_mut(token.as_key())
.unwrap()
};
let Assigned {
assigned: _assigned,
replaced: _replaced,
mut to,
} = self.assign_value(assigned, src)?;
let last_token = if let Some(i) = idx { i.into() } else { token };
to.push_front(last_token);
Ok(Assigned {
assigned,
replaced,
to,
})
}
fn create_next(
&self,
dest: &mut Value,
token: &Token,
idx: Option<usize>,
) -> Result<Option<Value>, Error> {
let next_token = self.front().unwrap();
let next = if next_token.as_index(0).is_ok() {
Value::Array(vec![])
} else {
Value::Object(serde_json::Map::new())
};
if let Some(idx) = idx {
let len = dest.as_array().unwrap().len();
if idx == len {
dest.as_array_mut().unwrap().push(next);
Ok(None)
} else {
let prev = dest.as_array_mut().unwrap().get_mut(idx).unwrap().clone();
dest.as_array_mut().unwrap()[idx] = next;
Ok(Some(prev))
}
} else {
let prev = dest
.as_object_mut()
.unwrap()
.insert(token.to_string(), next);
Ok(prev)
}
}
fn try_resolve_mut<'v>(&self, value: &'v mut Value) -> Result<ResolvedMut<'v>, Error> {
let mut tokens = self.tokens();
let mut resolved = Pointer::default();
let res = tokens.try_fold((value, self.clone()), |(v, mut p), token| match v {
Value::Null => ControlFlow::Break((v, p)),
Value::Number(_) | Value::String(_) | Value::Bool(_) => ControlFlow::Break((v, p)),
Value::Array(_) => match token.as_index(v.as_array_mut().unwrap().len()) {
Ok(idx) => {
if idx < v.as_array_mut().unwrap().len() {
let t = p.pop_front();
if let Some(t) = t {
resolved.push_back(t);
}
ControlFlow::Continue((v.as_array_mut().unwrap().get_mut(idx).unwrap(), p))
} else {
ControlFlow::Break((v, p))
}
}
Err(_) => ControlFlow::Break((v, p)),
},
Value::Object(_) => {
if v.as_object_mut().unwrap().contains_key(&*token) {
let t = p.pop_front();
if let Some(t) = t {
resolved.push_back(t);
}
ControlFlow::Continue((
v.as_object_mut().unwrap().get_mut(token.as_key()).unwrap(),
p,
))
} else {
ControlFlow::Break((v, p))
}
}
});
match res {
ControlFlow::Continue((v, remaining)) => Ok(ResolvedMut {
value: v,
remaining,
resolved,
}),
ControlFlow::Break((value, remaining)) => match value {
Value::Null | Value::Object(_) => Ok(ResolvedMut {
value,
remaining,
resolved,
}),
Value::Array(_) => match remaining.first() {
Some(token) => {
let len = value.as_array().unwrap().len();
let idx = token.as_index(len)?;
if idx <= len {
Ok(ResolvedMut {
value,
remaining,
resolved,
})
} else {
Err(OutOfBoundsError {
index: idx,
len,
token,
}
.into())
}
}
None => Ok(ResolvedMut {
value,
remaining,
resolved,
}),
},
Value::Bool(_) | Value::Number(_) | Value::String(_) => Ok(ResolvedMut {
value,
remaining,
resolved,
}),
},
}
}
fn try_resolve<'v>(&self, value: &'v Value) -> Result<Resolved<'v>, Error> {
let mut tokens = self.tokens();
let mut resolved = Pointer::default();
let res = tokens.try_fold((value, self.clone()), |(v, mut p), token| match v {
Value::Null => ControlFlow::Break((v, p)),
Value::Number(_) | Value::String(_) | Value::Bool(_) => ControlFlow::Break((v, p)),
Value::Array(_) => match token.as_index(v.as_array().unwrap().len()) {
Ok(idx) => {
if idx < v.as_array().unwrap().len() {
let t = p.pop_front();
if let Some(t) = t {
resolved.push_back(t);
}
ControlFlow::Continue((v.as_array().unwrap().get(idx).unwrap(), p))
} else {
ControlFlow::Break((v, p))
}
}
Err(_) => ControlFlow::Break((v, p)),
},
Value::Object(_) => {
if v.as_object().unwrap().contains_key(&*token) {
let t = p.pop_front();
if let Some(t) = t {
resolved.push_back(t);
}
ControlFlow::Continue((v.as_object().unwrap().get(&*token).unwrap(), p))
} else {
ControlFlow::Break((v, p))
}
}
});
match res {
ControlFlow::Continue((v, remaining)) => Ok(Resolved {
value: v,
remaining,
resolved,
}),
ControlFlow::Break((value, remaining)) => match value {
Value::Null | Value::Object(_) => Ok(Resolved {
value,
remaining,
resolved,
}),
Value::Array(_) => match remaining.first() {
Some(token) => {
let len = value.as_array().unwrap().len();
let idx = token.as_index(len)?;
if idx <= len {
Ok(Resolved {
value,
remaining,
resolved,
})
} else {
Err(OutOfBoundsError {
index: idx,
len,
token,
}
.into())
}
}
None => Ok(Resolved {
value,
remaining,
resolved,
}),
},
Value::Bool(_) | Value::Number(_) | Value::String(_) => Ok(Resolved {
value,
remaining,
resolved,
}),
},
}
}
fn split(&self) -> Split<'_, char> {
let mut s = self.inner.split('/');
s.next();
s
}
}
impl Eq for Pointer {}
impl Deref for Pointer {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl Serialize for Pointer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
String::serialize(&self.inner, serializer)
}
}
impl<'de> Deserialize<'de> for Pointer {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
let s = String::deserialize(deserializer)?;
match Pointer::try_from(s.as_str()) {
Ok(p) => Ok(p),
Err(err) => Err(D::Error::custom(err)),
}
}
}
impl AsRef<str> for Pointer {
fn as_ref(&self) -> &str {
&self.inner
}
}
impl Borrow<str> for Pointer {
fn borrow(&self) -> &str {
&self.inner
}
}
impl From<Token> for Pointer {
fn from(t: Token) -> Self {
Pointer::new([t])
}
}
impl PartialEq<&str> for Pointer {
fn eq(&self, other: &&str) -> bool {
&self.inner == other
}
}
impl PartialEq<str> for Pointer {
fn eq(&self, other: &str) -> bool {
self.inner == other
}
}
impl PartialEq<Pointer> for Pointer {
fn eq(&self, other: &Pointer) -> bool {
self.inner == other.inner
}
}
impl PartialEq<Pointer> for str {
fn eq(&self, other: &Pointer) -> bool {
self == other.inner
}
}
impl PartialEq<String> for Pointer {
fn eq(&self, other: &String) -> bool {
&self.inner == other
}
}
impl TryFrom<String> for Pointer {
type Error = MalformedPointerError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::try_from(value.as_str())
}
}
impl AsRef<[u8]> for Pointer {
fn as_ref(&self) -> &[u8] {
self.inner.as_bytes()
}
}
impl Borrow<[u8]> for Pointer {
fn borrow(&self) -> &[u8] {
self.inner.as_bytes()
}
}
#[cfg(feature = "uniresid")]
impl TryFrom<&uniresid::Uri> for Pointer {
type Error = MalformedPointerError;
fn try_from(uri: &uniresid::Uri) -> Result<Self, Self::Error> {
match uri.fragment_to_string() {
Ok(Some(_)) => todo!(),
Ok(None) => todo!(),
Err(err) => Err(err.into()),
}
}
}
#[cfg(feature = "uniresid")]
impl TryFrom<uniresid::Uri> for Pointer {
type Error = MalformedPointerError;
fn try_from(uri: uniresid::Uri) -> Result<Self, Self::Error> {
Self::try_from(&uri)
}
}
#[cfg(feature = "uniresid")]
impl TryFrom<&uniresid::AbsoluteUri> for Pointer {
type Error = MalformedPointerError;
fn try_from(uri: &uniresid::AbsoluteUri) -> Result<Self, Self::Error> {
match uri.fragment_to_string() {
Ok(Some(_)) => todo!(),
Ok(None) => todo!(),
Err(err) => Err(err.into()),
}
}
}
#[cfg(feature = "uniresid")]
impl TryFrom<uniresid::AbsoluteUri> for Pointer {
type Error = MalformedPointerError;
fn try_from(uri: uniresid::AbsoluteUri) -> Result<Self, Self::Error> {
Self::try_from(&uri)
}
}
#[cfg(feature = "url")]
impl TryFrom<&url::Url> for Pointer {
type Error = MalformedPointerError;
fn try_from(url: &url::Url) -> Result<Self, Self::Error> {
match url.fragment() {
Some(f) => Self::try_from(f),
None => Ok(Pointer::default()),
}
}
}
#[cfg(feature = "url")]
impl TryFrom<url::Url> for Pointer {
type Error = MalformedPointerError;
fn try_from(url: url::Url) -> Result<Self, Self::Error> {
match url.fragment() {
Some(f) => Self::try_from(f),
None => Ok(Pointer::default()),
}
}
}
#[cfg(feature = "fluent-uri")]
impl TryFrom<&fluent_uri::Uri<&str>> for Pointer {
type Error = MalformedPointerError;
fn try_from(uri: &fluent_uri::Uri<&str>) -> Result<Self, Self::Error> {
match uri.fragment() {
Some(f) => Self::try_from(f.as_str()),
None => Ok(Pointer::default()),
}
}
}
#[cfg(feature = "fluent-uri")]
impl TryFrom<&fluent_uri::Uri<String>> for Pointer {
type Error = MalformedPointerError;
fn try_from(uri: &fluent_uri::Uri<String>) -> Result<Self, Self::Error> {
match uri.fragment() {
Some(f) => Self::try_from(f.as_str()),
None => Ok(Pointer::default()),
}
}
}
#[cfg(feature = "fluent-uri")]
impl TryFrom<&fluent_uri::Uri<&mut [u8]>> for Pointer {
type Error = MalformedPointerError;
fn try_from(uri: &fluent_uri::Uri<&mut [u8]>) -> Result<Self, Self::Error> {
match uri.fragment() {
Some(f) => Self::try_from(f.as_str()),
None => Ok(Pointer::default()),
}
}
}
impl From<Pointer> for Value {
fn from(ptr: Pointer) -> Self {
ptr.to_value()
}
}
impl From<&Pointer> for Value {
fn from(ptr: &Pointer) -> Self {
ptr.to_value()
}
}
impl From<usize> for Pointer {
fn from(value: usize) -> Self {
Pointer::new([value])
}
}
impl<'a> IntoIterator for &'a Pointer {
type Item = Token;
type IntoIter = Tokens<'a>;
fn into_iter(self) -> Self::IntoIter {
self.tokens()
}
}
impl TryFrom<&str> for Pointer {
type Error = MalformedPointerError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let (count, inner) = validate_and_format(value)?;
Ok(Pointer { count, inner })
}
}
fn validate_and_format(value: &str) -> Result<(usize, String), MalformedPointerError> {
let mut chars = value.chars();
match chars.next() {
Some('#') => {
let next = chars.next();
if next.is_none() {
return Ok((0, String::default()));
}
if next != Some('/') {
return Err(MalformedPointerError::NoLeadingSlash(value.into()));
}
}
Some('/') => {}
Some(_) => {
return Err(MalformedPointerError::NoLeadingSlash(value.into()));
}
None => {
return Ok((0, String::default()));
}
}
let mut res = String::with_capacity(value.len());
res.push('/');
let mut count = 1; while let Some(c) = chars.next() {
res.push(c);
match c {
'~' => match chars.next() {
Some('0') => res.push('0'),
Some('1') => res.push('1'),
_ => {
return Err(MalformedPointerError::InvalidEncoding(value.to_string()));
}
},
'/' => {
count += 1;
}
_ => {}
}
}
Ok((count, res))
}
impl PartialOrd<&str> for Pointer {
fn partial_cmp(&self, other: &&str) -> Option<Ordering> {
PartialOrd::partial_cmp(&self.inner[..], &other[..])
}
}
impl PartialOrd<String> for Pointer {
fn partial_cmp(&self, other: &String) -> Option<Ordering> {
self.inner.partial_cmp(other)
}
}
impl PartialOrd<Pointer> for Pointer {
fn partial_cmp(&self, other: &Pointer) -> Option<Ordering> {
self.inner.partial_cmp(&other.inner)
}
}
impl PartialEq<Pointer> for String {
fn eq(&self, other: &Pointer) -> bool {
PartialEq::eq(&self[..], &other.inner[..])
}
}
impl PartialOrd<Pointer> for String {
fn partial_cmp(&self, other: &Pointer) -> Option<Ordering> {
self.partial_cmp(&other.inner)
}
}
impl core::hash::Hash for Pointer {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.inner.hash(state)
}
}
impl core::fmt::Debug for Pointer {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "\"{}\"", self.inner)
}
}
impl core::fmt::Display for Pointer {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.inner)
}
}
impl FromStr for Pointer {
type Err = MalformedPointerError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(s)
}
}
#[derive(Debug)]
struct Assigned<'a> {
assigned: &'a mut Value,
replaced: Value,
to: Pointer,
}
#[derive(Debug)]
struct ResolvedMut<'a> {
value: &'a mut Value,
remaining: Pointer,
resolved: Pointer,
}
#[derive(Debug)]
struct Resolved<'a> {
value: &'a Value,
remaining: Pointer,
resolved: Pointer,
}
#[cfg(test)]
mod tests {
use serde_json::json;
use crate::{Resolve, ResolveMut};
use super::*;
#[test]
fn test_rfc_examples() {
let data = json!({
"foo": ["bar", "baz"],
"": 0,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
" ": 7,
"m~n": 8
});
let val = data.get("").unwrap();
assert_eq!(val, 0);
let ptr = Pointer::default();
assert_eq!(data.resolve(&ptr).unwrap(), &data);
let ptr = Pointer::try_from("/foo").unwrap();
assert_eq!(data.resolve(&ptr).unwrap(), &json!(["bar", "baz"]));
let ptr = Pointer::try_from("/foo/0").unwrap();
assert_eq!(data.resolve(&ptr).unwrap(), &json!("bar"));
let ptr = Pointer::try_from("/").unwrap();
assert_eq!(data.resolve(&ptr).unwrap(), &json!(0));
assert_eq!(data.get("a/b").unwrap(), 1);
let ptr = Pointer::try_from("/a~1b").unwrap();
assert_eq!(ptr.as_str(), "/a~1b");
assert_eq!(data.get("a/b").unwrap(), 1);
assert_eq!(&ptr.first().unwrap(), "a/b");
assert_eq!(data.resolve(&ptr).unwrap(), &json!(1));
let ptr = Pointer::try_from("/c%d").unwrap();
assert_eq!(data.resolve(&ptr).unwrap(), &json!(2));
let ptr = Pointer::try_from("/e^f").unwrap();
assert_eq!(data.resolve(&ptr).unwrap(), &json!(3));
let ptr = Pointer::try_from("/g|h").unwrap();
assert_eq!(data.resolve(&ptr).unwrap(), &json!(4));
let ptr = Pointer::try_from("/i\\j").unwrap();
assert_eq!(data.resolve(&ptr).unwrap(), &json!(5));
let ptr = Pointer::try_from("/k\"l").unwrap();
assert_eq!(data.resolve(&ptr).unwrap(), &json!(6));
let ptr = Pointer::try_from("/ ").unwrap();
assert_eq!(data.resolve(&ptr).unwrap(), &json!(7));
let ptr = Pointer::try_from("/m~0n").unwrap();
assert_eq!(data.resolve(&ptr).unwrap(), &json!(8));
}
#[test]
fn test_try_from_validation() {
assert!(Pointer::try_from("").is_ok());
assert!(Pointer::try_from("/").is_ok());
assert!(Pointer::try_from("/foo").is_ok());
let res = Pointer::try_from("/foo~");
assert!(res.is_err());
let err = res.unwrap_err();
assert_eq!(
err,
MalformedPointerError::InvalidEncoding("/foo~".to_string())
);
let res = Pointer::try_from("foo");
assert!(res.is_err());
let err = res.unwrap_err();
assert_eq!(
err,
MalformedPointerError::NoLeadingSlash("foo".to_string())
);
assert!(Pointer::try_from("/foo/bar/baz/~1/~0").is_ok());
assert_eq!(
&Pointer::try_from("/foo/bar/baz/~1/~0").unwrap(),
"/foo/bar/baz/~1/~0"
);
}
#[test]
fn test_push_pop_back() {
let mut ptr = Pointer::default();
assert_eq!(ptr, "", "default, root pointer should equal \"\"");
assert_eq!(ptr.count(), 0, "default pointer should have 0 tokens");
ptr.push_back("foo".into());
assert_eq!(ptr, "/foo", "pointer should equal \"/foo\" after push_back");
ptr.push_back("bar".into());
assert_eq!(
ptr, "/foo/bar",
"pointer should equal \"/foo/bar\" after push_back"
);
ptr.push_back("/baz".into());
assert_eq!(
ptr, "/foo/bar/~1baz",
"pointer should equal \"/foo/bar/~1baz\" after push_back"
);
let mut ptr = Pointer::new(["foo", "bar"]);
assert_eq!(ptr.pop_back(), Some("bar".into()));
assert_eq!(ptr, "/foo", "pointer should equal \"/foo\" after pop_back");
assert_eq!(
ptr.pop_back(),
Some("foo".into()),
"\"foo\" should have been popped from the back"
);
assert_eq!(ptr, "", "pointer should equal \"\" after pop_back");
}
#[test]
fn test_replace_token() {
let mut ptr = Pointer::try_from("/test/token").unwrap();
let res = ptr.replace_token(0, "new".into());
assert!(res.is_ok());
assert_eq!(
ptr, "/new/token",
"pointer should equal \"/new/token\" after replace_token"
);
let res = ptr.replace_token(3, "invalid".into());
assert!(res.is_err());
}
#[test]
fn test_push_pop_front() {
let mut ptr = Pointer::default();
assert_eq!(ptr, "");
assert_eq!(ptr.count(), 0);
ptr.push_front("bar".into());
assert_eq!(ptr, "/bar");
assert_eq!(ptr.count(), 1);
ptr.push_front("foo".into());
assert_eq!(ptr, "/foo/bar");
assert_eq!(ptr.count(), 2);
ptr.push_front("too".into());
assert_eq!(ptr, "/too/foo/bar");
assert_eq!(ptr.count(), 3);
assert_eq!(ptr.pop_front(), Some("too".into()));
assert_eq!(ptr, "/foo/bar");
assert_eq!(ptr.count(), 2);
assert_eq!(ptr.pop_back(), Some("bar".into()));
assert_eq!(ptr, "/foo");
assert_eq!(ptr.count(), 1);
assert_eq!(ptr.pop_front(), Some("foo".into()));
assert_eq!(ptr, "");
}
#[test]
fn pop_front_works_with_empty_strings() {
{
let mut ptr = Pointer::new(["bar", "", ""]);
assert_eq!(ptr.tokens().count(), 3);
let mut token = ptr.pop_front();
assert_eq!(token, Some(Token::from_encoded("bar")));
assert_eq!(ptr.tokens().count(), 2);
token = ptr.pop_front();
assert_eq!(token, Some(Token::from_encoded("")));
assert_eq!(ptr.tokens().count(), 1);
token = ptr.pop_front();
assert_eq!(token, Some(Token::from_encoded("")));
assert_eq!(ptr.tokens().count(), 0);
assert_eq!(ptr, Pointer::root());
}
{
let mut ptr = Pointer::root();
assert_eq!(ptr.tokens().count(), 0);
ptr.push_back("".into());
assert_eq!(ptr.tokens().count(), 1);
ptr.pop_back();
assert_eq!(ptr.tokens().count(), 0);
}
{
let mut ptr = Pointer::root();
let input = ["", "", "", "foo", "", "bar", "baz", ""];
for (idx, s) in input.iter().enumerate() {
assert_eq!(ptr.tokens().count(), idx);
ptr.push_back(s.into());
}
assert_eq!(ptr.tokens().count(), input.len());
for (idx, s) in input.iter().enumerate() {
assert_eq!(ptr.tokens().count(), 8 - idx);
assert_eq!(ptr.front().unwrap().as_str(), *s);
assert_eq!(ptr.pop_front().unwrap().as_str(), *s);
}
assert_eq!(ptr.tokens().count(), 0);
assert!(ptr.front().is_none());
assert!(ptr.pop_front().is_none());
}
}
#[test]
fn test_formatting() {
assert_eq!(Pointer::new(["foo", "bar"]), "/foo/bar");
assert_eq!(
Pointer::new(["~/foo", "~bar", "/baz"]),
"/~0~1foo/~0bar/~1baz"
);
assert_eq!(Pointer::new(["field", "", "baz"]), "/field//baz");
assert_eq!(Pointer::default(), "");
}
#[test]
fn test_last() {
let ptr = Pointer::try_from("/foo/bar").unwrap();
assert_eq!(ptr.last(), Some("bar".into()));
let ptr = Pointer::try_from("/foo/bar/-").unwrap();
assert_eq!(ptr.last(), Some("-".into()));
let ptr = Pointer::try_from("/-").unwrap();
assert_eq!(ptr.last(), Some("-".into()));
let ptr = Pointer::default();
assert_eq!(ptr.last(), None);
let ptr = Pointer::try_from("/bar").unwrap();
assert_eq!(ptr.last(), Some("bar".into()));
}
#[test]
fn test_first() {
let ptr = Pointer::try_from("/foo/bar").unwrap();
assert_eq!(ptr.first(), Some("foo".into()));
let ptr = Pointer::try_from("/foo/bar/-").unwrap();
assert_eq!(ptr.first(), Some("foo".into()));
let ptr = Pointer::default();
assert_eq!(ptr.first(), None);
}
fn test_data() -> serde_json::Value {
json!({
"foo": {
"bar": {
"baz": {
"qux": "quux"
}
},
"strings": ["zero", "one", "two"],
"nothing": null,
"bool": true,
"objects": [{
"field": "zero"
}, {
"field": "one"
}, {
"field": "two"
}]
}
})
}
#[test]
fn test_try_resolve_mut() {
let mut data = test_data();
let ptr = Pointer::try_from("/foo/bar/baz/qux").unwrap();
let ResolvedMut {
value,
remaining,
resolved: _resolved,
} = ptr.try_resolve_mut(&mut data).unwrap();
assert_eq!(&remaining, "");
assert_eq!(value, &mut json!("quux"));
let ptr = Pointer::try_from("/foo/bar/does_not_exist/derp").unwrap();
let ResolvedMut {
value,
remaining,
resolved: _resolved,
} = ptr.try_resolve_mut(&mut data).unwrap();
assert_eq!(remaining, "/does_not_exist/derp");
assert_eq!(
value,
&mut json!({
"baz": {
"qux": "quux"
}
})
);
let ptr = Pointer::try_from("/foo/strings/0").unwrap();
let res = ptr.try_resolve_mut(&mut data).unwrap();
assert_eq!(res.remaining, "");
assert_eq!(res.value, &mut json!("zero"));
let ptr = Pointer::try_from("/foo/strings/1").unwrap();
let res = ptr.try_resolve_mut(&mut data).unwrap();
assert_eq!(res.remaining, "");
assert_eq!(res.value, &mut json!("one"));
let ptr = Pointer::try_from("/foo/strings/2").unwrap();
let res = ptr.try_resolve_mut(&mut data).unwrap();
assert_eq!(res.remaining, "");
assert_eq!(res.value, &mut json!("two"));
let ptr = Pointer::try_from("/foo/strings/-").unwrap();
let res = ptr.try_resolve_mut(&mut data).unwrap();
assert_eq!(res.remaining, "/-");
assert_eq!(res.value, &mut json!(["zero", "one", "two"]));
let ptr = Pointer::try_from("/foo/strings/0").unwrap();
let res = ptr.try_resolve_mut(&mut data).unwrap();
assert_eq!(res.remaining, "");
assert_eq!(res.value, &mut json!("zero"));
}
#[test]
fn test_try_resolve_mut_overflow_error() {
let mut data = test_data();
let ptr = Pointer::try_from("/foo/strings/7").unwrap();
let res = ptr.try_resolve_mut(&mut data);
assert!(res.is_err());
}
#[test]
fn test_resolve_unresolvable() {
let mut data = test_data();
let ptr = Pointer::try_from("/foo/bool/unresolvable").unwrap();
let res = ptr.resolve_mut(&mut data);
assert!(res.is_err());
let err = res.unwrap_err();
assert!(err.is_unresolvable());
}
#[test]
fn test_resolve_not_found() {
let mut data = test_data();
let ptr = Pointer::new(["foo", "not_found", "nope"]);
let res = ptr.resolve_mut(&mut data);
assert!(res.is_err());
let err = res.unwrap_err();
assert!(err.is_not_found());
match err {
Error::NotFound(err) => {
assert_eq!(err.pointer, "/foo/not_found");
}
_ => panic!("expected NotFound"),
}
}
#[test]
fn test_try_resolve_mut_unresolvable() {
let mut data = test_data();
let ptr = Pointer::try_from("/foo/bool/unresolvable").unwrap();
let res = ptr.try_resolve_mut(&mut data).unwrap();
assert_eq!(res.remaining, "/unresolvable");
assert_eq!(res.resolved, "/foo/bool");
assert!(res.value.is_boolean());
}
#[test]
fn test_try_from() {
let ptr = Pointer::new(["foo", "bar", "~/"]);
assert_eq!(Pointer::try_from("/foo/bar/~0~1").unwrap(), ptr);
let into: Pointer = "/foo/bar/~0~1".try_into().unwrap();
assert_eq!(ptr, into);
}
#[test]
fn test_try_resolve() {
let data = test_data();
let ptr = Pointer::try_from("/foo/bar/baz/qux").unwrap();
let res = ptr.try_resolve(&data).unwrap();
assert_eq!(&res.remaining, "");
assert_eq!(res.value, &mut json!("quux"));
let ptr = Pointer::try_from("/foo/bar/does_not_exist/derp").unwrap();
let res = ptr.try_resolve(&data).unwrap();
assert_eq!(res.remaining, "/does_not_exist/derp");
assert_eq!(
res.value,
&mut json!({
"baz": {
"qux": "quux"
}
})
);
let ptr = Pointer::try_from("/foo/strings/0").unwrap();
let res = ptr.try_resolve(&data).unwrap();
assert_eq!(res.remaining, "");
assert_eq!(res.value, &mut json!("zero"));
let ptr = Pointer::try_from("/foo/strings/1").unwrap();
let res = ptr.try_resolve(&data).unwrap();
assert_eq!(res.remaining, "");
assert_eq!(res.value, &mut json!("one"));
let ptr = Pointer::try_from("/foo/strings/2").unwrap();
let res = ptr.try_resolve(&data).unwrap();
assert_eq!(res.remaining, "");
assert_eq!(res.value, &mut json!("two"));
let ptr = Pointer::try_from("/foo/strings/-").unwrap();
let res = ptr.try_resolve(&data).unwrap();
assert_eq!(res.remaining, "/-");
assert_eq!(res.value, &mut json!(["zero", "one", "two"]));
let ptr = Pointer::try_from("/foo/strings/0").unwrap();
let res = ptr.try_resolve(&data).unwrap();
assert_eq!(res.remaining, "");
assert_eq!(res.value, &mut json!("zero"));
}
#[test]
fn test_try_resolve_overflow_error() {
let data = test_data();
let ptr = Pointer::try_from("/foo/strings/7").unwrap();
let res = ptr.try_resolve(&data);
assert!(res.is_err());
}
#[test]
fn test_try_resolve_unresolvable_error() {
let data = test_data();
let ptr = Pointer::try_from("/foo/bool/unresolvable/not-in-token").unwrap();
let res = ptr.try_resolve(&data).unwrap();
assert_eq!(
res.remaining,
Pointer::try_from("/unresolvable/not-in-token").unwrap()
);
}
#[test]
fn test_resolve_mut_unresolvable_error() {
let mut data = test_data();
let ptr = Pointer::try_from("/foo/bool/unresolvable/not-in-token").unwrap();
let res = ptr.resolve_mut(&mut data);
assert!(res.is_err());
let unresolvable = "/foo/bool/unresolvable".try_into().unwrap();
assert_eq!(
res.err().unwrap(),
UnresolvableError::new(unresolvable).into()
);
let mut data = json!({"foo": "bar"});
let ptr = Pointer::try_from("/foo/unresolvable").unwrap();
let err = data.resolve_mut(&ptr).unwrap_err();
assert_eq!(err, UnresolvableError::new(ptr.clone()).into());
}
#[test]
fn test_resolve_unresolvable_error() {
let data = test_data();
let ptr = Pointer::try_from("/foo/bool/unresolvable/not-in-token").unwrap();
let res = ptr.resolve(&data);
assert!(res.is_err());
let unresolvable = "/foo/bool/unresolvable".try_into().unwrap();
assert_eq!(
res.err().unwrap(),
UnresolvableError::new(unresolvable).into()
)
}
#[test]
fn test_assign() {
let mut data = json!({});
let ptr = Pointer::try_from("/foo").unwrap();
let val = json!("bar");
let assignment = ptr.assign(&mut data, val.clone()).unwrap();
assert_eq!(assignment.replaced, Value::Null);
assert_eq!(assignment.assigned.clone().into_owned(), val);
assert_eq!(assignment.assigned_to, "/foo");
let val2 = json!("baz");
let assignment = ptr.assign(&mut data, val2.clone()).unwrap();
assert_eq!(assignment.replaced, Value::String("bar".to_string()));
assert_eq!(assignment.assigned.clone().into_owned(), val2);
assert_eq!(assignment.assigned_to, "/foo");
}
#[test]
fn test_assign_with_explicit_array_path() {
let mut data = json!({});
let ptr = Pointer::try_from("/foo/0/bar").unwrap();
let val = json!("baz");
let assignment = ptr.assign(&mut data, val).unwrap();
assert_eq!(assignment.replaced, Value::Null);
assert_eq!(assignment.assigned_to, "/foo/0/bar");
assert_eq!(assignment.replaced, Value::Null);
assert_eq!(
json!({
"foo": [
{
"bar": "baz"
}
]
}),
data.clone()
);
}
#[test]
fn test_assign_array_with_next_token() {
let mut data = json!({});
let ptr = Pointer::try_from("/foo/-/bar").unwrap();
let val = json!("baz");
let assignment = ptr.assign(&mut data, val).unwrap();
assert_eq!(assignment.replaced, Value::Null);
assert_eq!(
assignment.assigned_to, "/foo/0/bar",
"`assigned_to` should equal \"/foo/0/bar\""
);
assert_eq!(assignment.replaced, Value::Null);
assert_eq!(
json!({
"foo": [
{
"bar": "baz"
}
]
}),
data.clone()
);
let val = json!("baz2");
let assignment = ptr.assign(&mut data, val).unwrap();
assert_eq!(assignment.replaced, Value::Null);
assert_eq!(
assignment.assigned_to, "/foo/1/bar",
"`assigned_to` should equal \"/foo/1/bar\""
);
assert_eq!(assignment.replaced, Value::Null);
assert_eq!(
json!({
"foo": [
{
"bar": "baz"
},
{
"bar": "baz2"
}
]
}),
data.clone()
);
let ptr = Pointer::try_from("/foo/0/bar").unwrap();
let val = json!("qux");
let assignment = ptr.assign(&mut data, val).unwrap();
assert_eq!(assignment.replaced, Value::String("baz".to_string()));
assert_eq!(
json!({
"foo": [
{
"bar": "qux"
},
{
"bar": "baz2"
}
]
}),
data.clone()
);
}
#[test]
fn test_assign_with_obj_path() {
let mut data = json!({});
let ptr = Pointer::try_from("/foo/bar").unwrap();
let val = json!("baz");
let assignment = ptr.assign(&mut data, val).unwrap();
assert_eq!(assignment.assigned_to, "/foo/bar");
assert_eq!(assignment.replaced, Value::Null);
assert_eq!(
json!({
"foo": {
"bar": "baz"
}
}),
data.clone()
);
}
#[test]
fn test_assign_with_scalar_replace() {
let mut data = json!({
"foo": "bar"
});
let ptr = Pointer::try_from("/foo/bar/baz").unwrap();
let val = json!("qux");
ptr.assign(&mut data, val).unwrap();
assert_eq!(
json!({
"foo": {
"bar": {
"baz": "qux"
}
}
}),
data.clone()
);
}
#[test]
fn nested_maps_with_empty_keys() {
let data = json!({
"": {
"": {
"bar": 42,
}
}
});
{
let ptr = Pointer::try_from("///bar").unwrap();
assert_eq!(ptr.resolve(&data).unwrap(), 42);
}
{
let mut ptr = Pointer::root();
ptr.push_back("".into());
ptr.push_back("".into());
ptr.push_back("bar".into());
assert_eq!(ptr.resolve(&data).unwrap(), 42);
}
}
#[test]
fn pop_back_works_with_empty_strings() {
{
let mut ptr = Pointer::root();
ptr.push_back("".into());
ptr.push_back("".into());
ptr.push_back("bar".into());
assert_eq!(ptr.tokens().count(), 3);
ptr.pop_back();
assert_eq!(ptr.tokens().count(), 2);
ptr.pop_back();
assert_eq!(ptr.tokens().count(), 1);
ptr.pop_back();
assert_eq!(ptr.tokens().count(), 0);
assert_eq!(ptr, Pointer::root());
}
{
let mut ptr = Pointer::root();
assert_eq!(ptr.tokens().count(), 0);
ptr.push_back("".into());
assert_eq!(ptr.tokens().count(), 1);
ptr.pop_back();
assert_eq!(ptr.tokens().count(), 0);
}
{
let mut ptr = Pointer::root();
let input = ["", "", "", "foo", "", "bar", "baz", ""];
for (idx, s) in input.iter().enumerate() {
assert_eq!(ptr.tokens().count(), idx);
ptr.push_back(s.into());
}
assert_eq!(ptr.tokens().count(), input.len());
for (idx, s) in input.iter().enumerate().rev() {
assert_eq!(ptr.tokens().count(), idx + 1);
assert_eq!(ptr.back().unwrap().as_str(), *s);
assert_eq!(ptr.pop_back().unwrap().as_str(), *s);
}
assert_eq!(ptr.tokens().count(), 0);
assert!(ptr.back().is_none());
assert!(ptr.pop_back().is_none());
}
}
#[test]
#[cfg(feature = "fluent-uri")]
fn test_try_from_fluent_uri() {
let uri = fluent_uri::Uri::parse("#/foo/bar").unwrap();
let ptr = Pointer::try_from(&uri).unwrap();
assert_eq!(ptr, "/foo/bar");
}
}