use crate::{
index::{OutOfBoundsError, ParseIndexError},
Pointer, Token,
};
pub trait Resolve {
type Value;
type Error;
fn resolve(&self, ptr: &Pointer) -> Result<&Self::Value, Self::Error>;
}
pub trait ResolveMut {
type Value;
type Error;
fn resolve_mut(&mut self, ptr: &Pointer) -> Result<&mut Self::Value, Self::Error>;
}
#[derive(Debug, PartialEq, Eq)]
pub enum ResolveError {
FailedToParseIndex {
offset: usize,
source: ParseIndexError,
},
OutOfBounds {
offset: usize,
source: OutOfBoundsError,
},
NotFound {
offset: usize,
},
Unreachable {
offset: usize,
},
}
impl ResolveError {
pub fn offset(&self) -> usize {
match self {
Self::FailedToParseIndex { offset, .. }
| Self::OutOfBounds { offset, .. }
| Self::NotFound { offset, .. }
| Self::Unreachable { offset, .. } => *offset,
}
}
pub fn is_unreachable(&self) -> bool {
matches!(self, Self::Unreachable { .. })
}
pub fn is_not_found(&self) -> bool {
matches!(self, Self::NotFound { .. })
}
pub fn is_out_of_bounds(&self) -> bool {
matches!(self, Self::OutOfBounds { .. })
}
pub fn is_failed_to_parse_index(&self) -> bool {
matches!(self, Self::FailedToParseIndex { .. })
}
}
impl core::fmt::Display for ResolveError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::FailedToParseIndex { offset, .. } => {
write!(f, "failed to parse index at offset {offset}")
}
Self::OutOfBounds { offset, .. } => {
write!(f, "index at offset {offset} out of bounds")
}
Self::NotFound { offset, .. } => {
write!(f, "pointer starting at offset {offset} not found")
}
Self::Unreachable { offset, .. } => {
write!(f, "pointer starting at offset {offset} is unreachable")
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ResolveError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::FailedToParseIndex { source, .. } => Some(source),
Self::OutOfBounds { source, .. } => Some(source),
_ => None,
}
}
}
#[cfg(feature = "json")]
mod json {
use super::{parse_index, Pointer, Resolve, ResolveError, ResolveMut};
use serde_json::Value;
impl Resolve for Value {
type Value = Value;
type Error = ResolveError;
fn resolve(&self, mut ptr: &Pointer) -> Result<&Value, Self::Error> {
let mut offset = 0;
let mut value = self;
while let Some((token, rem)) = ptr.split_front() {
let tok_len = token.encoded().len();
ptr = rem;
value = match value {
Value::Array(v) => {
let idx = token
.to_index()
.map_err(|source| ResolveError::FailedToParseIndex { offset, source })?
.for_len(v.len())
.map_err(|source| ResolveError::OutOfBounds { offset, source })?;
Ok(&v[idx])
}
Value::Object(v) => v
.get(token.decoded().as_ref())
.ok_or(ResolveError::NotFound { offset }),
_ => Err(ResolveError::Unreachable { offset }),
}?;
offset += 1 + tok_len;
}
Ok(value)
}
}
impl ResolveMut for Value {
type Value = Value;
type Error = ResolveError;
fn resolve_mut(&mut self, mut ptr: &Pointer) -> Result<&mut Value, ResolveError> {
let mut offset = 0;
let mut value = self;
while let Some((token, rem)) = ptr.split_front() {
let tok_len = token.encoded().len();
ptr = rem;
value = match value {
Value::Array(array) => {
let idx = parse_index(token, array.len(), offset)?;
Ok(&mut array[idx])
}
Value::Object(v) => v
.get_mut(token.decoded().as_ref())
.ok_or(ResolveError::NotFound { offset }),
_ => Err(ResolveError::Unreachable { offset }),
}?;
offset += 1 + tok_len;
}
Ok(value)
}
}
}
fn parse_index(token: Token, array_len: usize, offset: usize) -> Result<usize, ResolveError> {
token
.to_index()
.map_err(|source| ResolveError::FailedToParseIndex { offset, source })?
.for_len(array_len)
.map_err(|source| ResolveError::OutOfBounds { offset, source })
}
#[cfg(feature = "toml")]
mod toml {
use super::{Resolve, ResolveError, ResolveMut};
use crate::Pointer;
use toml::Value;
impl Resolve for Value {
type Value = Value;
type Error = ResolveError;
fn resolve(&self, mut ptr: &Pointer) -> Result<&Value, Self::Error> {
let mut offset = 0;
let mut value = self;
while let Some((token, rem)) = ptr.split_front() {
let tok_len = token.encoded().len();
ptr = rem;
value = match value {
Value::Array(v) => {
let idx = token
.to_index()
.map_err(|source| ResolveError::FailedToParseIndex { offset, source })?
.for_len(v.len())
.map_err(|source| ResolveError::OutOfBounds { offset, source })?;
Ok(&v[idx])
}
Value::Table(v) => v
.get(token.decoded().as_ref())
.ok_or(ResolveError::NotFound { offset }),
_ => Err(ResolveError::Unreachable { offset }),
}?;
offset += 1 + tok_len;
}
Ok(value)
}
}
impl ResolveMut for Value {
type Value = Value;
type Error = ResolveError;
fn resolve_mut(&mut self, mut ptr: &Pointer) -> Result<&mut Value, ResolveError> {
let mut offset = 0;
let mut value = self;
while let Some((token, rem)) = ptr.split_front() {
let tok_len = token.encoded().len();
ptr = rem;
value = match value {
Value::Array(array) => {
let idx = token
.to_index()
.map_err(|source| ResolveError::FailedToParseIndex { offset, source })?
.for_len(array.len())
.map_err(|source| ResolveError::OutOfBounds { offset, source })?;
Ok(&mut array[idx])
}
Value::Table(v) => v
.get_mut(token.decoded().as_ref())
.ok_or(ResolveError::NotFound { offset }),
_ => Err(ResolveError::Unreachable { offset }),
}?;
offset += 1 + tok_len;
}
Ok(value)
}
}
}
#[cfg(test)]
mod tests {
use super::{Resolve, ResolveError, ResolveMut};
use crate::{
index::{OutOfBoundsError, ParseIndexError},
Pointer,
};
use core::fmt;
#[cfg(feature = "std")]
#[test]
fn resolve_error_source() {
use std::error::Error;
let err = ResolveError::FailedToParseIndex {
offset: 0,
source: ParseIndexError::InvalidInteger("invalid".parse::<usize>().unwrap_err()),
};
assert!(err.source().is_some());
let err = ResolveError::OutOfBounds {
offset: 0,
source: OutOfBoundsError {
index: 1,
length: 0,
},
};
assert!(err.source().is_some());
let err = ResolveError::NotFound { offset: 0 };
assert!(err.source().is_none());
let err = ResolveError::Unreachable { offset: 0 };
assert!(err.source().is_none());
}
#[test]
fn resolve_error_display() {
let err = ResolveError::FailedToParseIndex {
offset: 0,
source: ParseIndexError::InvalidInteger("invalid".parse::<usize>().unwrap_err()),
};
assert_eq!(format!("{err}"), "failed to parse index at offset 0");
let err = ResolveError::OutOfBounds {
offset: 0,
source: OutOfBoundsError {
index: 1,
length: 0,
},
};
assert_eq!(format!("{err}"), "index at offset 0 out of bounds");
let err = ResolveError::NotFound { offset: 0 };
assert_eq!(format!("{err}"), "pointer starting at offset 0 not found");
let err = ResolveError::Unreachable { offset: 0 };
assert_eq!(
format!("{err}"),
"pointer starting at offset 0 is unreachable"
);
}
#[test]
fn resolve_error_offset() {
let err = ResolveError::FailedToParseIndex {
offset: 0,
source: ParseIndexError::InvalidInteger("invalid".parse::<usize>().unwrap_err()),
};
assert_eq!(err.offset(), 0);
let err = ResolveError::OutOfBounds {
offset: 0,
source: OutOfBoundsError {
index: 1,
length: 0,
},
};
assert_eq!(err.offset(), 0);
let err = ResolveError::NotFound { offset: 0 };
assert_eq!(err.offset(), 0);
let err = ResolveError::Unreachable { offset: 0 };
assert_eq!(err.offset(), 0);
}
#[test]
fn resolve_error_is_unreachable() {
let err = ResolveError::FailedToParseIndex {
offset: 0,
source: ParseIndexError::InvalidInteger("invalid".parse::<usize>().unwrap_err()),
};
assert!(!err.is_unreachable());
let err = ResolveError::OutOfBounds {
offset: 0,
source: OutOfBoundsError {
index: 1,
length: 0,
},
};
assert!(!err.is_unreachable());
let err = ResolveError::NotFound { offset: 0 };
assert!(!err.is_unreachable());
let err = ResolveError::Unreachable { offset: 0 };
assert!(err.is_unreachable());
}
#[test]
fn resolve_error_is_not_found() {
let err = ResolveError::FailedToParseIndex {
offset: 0,
source: ParseIndexError::InvalidInteger("invalid".parse::<usize>().unwrap_err()),
};
assert!(!err.is_not_found());
let err = ResolveError::OutOfBounds {
offset: 0,
source: OutOfBoundsError {
index: 1,
length: 0,
},
};
assert!(!err.is_not_found());
let err = ResolveError::NotFound { offset: 0 };
assert!(err.is_not_found());
let err = ResolveError::Unreachable { offset: 0 };
assert!(!err.is_not_found());
}
#[test]
fn resolve_error_is_out_of_bounds() {
let err = ResolveError::FailedToParseIndex {
offset: 0,
source: ParseIndexError::InvalidInteger("invalid".parse::<usize>().unwrap_err()),
};
assert!(!err.is_out_of_bounds());
let err = ResolveError::OutOfBounds {
offset: 0,
source: OutOfBoundsError {
index: 1,
length: 0,
},
};
assert!(err.is_out_of_bounds());
let err = ResolveError::NotFound { offset: 0 };
assert!(!err.is_out_of_bounds());
let err = ResolveError::Unreachable { offset: 0 };
assert!(!err.is_out_of_bounds());
}
#[test]
fn resolve_error_is_failed_to_parse_index() {
let err = ResolveError::FailedToParseIndex {
offset: 0,
source: ParseIndexError::InvalidInteger("invalid".parse::<usize>().unwrap_err()),
};
assert!(err.is_failed_to_parse_index());
let err = ResolveError::OutOfBounds {
offset: 0,
source: OutOfBoundsError {
index: 1,
length: 0,
},
};
assert!(!err.is_failed_to_parse_index());
let err = ResolveError::NotFound { offset: 0 };
assert!(!err.is_failed_to_parse_index());
let err = ResolveError::Unreachable { offset: 0 };
assert!(!err.is_failed_to_parse_index());
}
#[test]
#[cfg(feature = "json")]
fn resolve_json() {
use serde_json::json;
let data = &json!({
"array": ["bar", "baz"],
"object": {
"object": {"baz": {"qux": "quux"}},
"strings": ["zero", "one", "two"],
"nothing": null,
"bool": true,
"objects": [{"field": "zero"}, {"field": "one"}, {"field": "two"}]
},
"": 0,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
" ": 7,
"m~n": 8
});
Test::all([
Test {
ptr: "",
data,
expected: Ok(data),
},
Test {
ptr: "/array",
data,
expected: Ok(data.get("array").unwrap()), },
Test {
ptr: "/array/0",
data,
expected: Ok(data.get("array").unwrap().get(0).unwrap()), },
Test {
ptr: "/a~1b",
data,
expected: Ok(data.get("a/b").unwrap()), },
Test {
ptr: "/c%d",
data,
expected: Ok(data.get("c%d").unwrap()), },
Test {
ptr: "/e^f",
data,
expected: Ok(data.get("e^f").unwrap()), },
Test {
ptr: "/g|h",
data,
expected: Ok(data.get("g|h").unwrap()), },
Test {
ptr: "/i\\j",
data,
expected: Ok(data.get("i\\j").unwrap()), },
Test {
ptr: "/k\"l",
data,
expected: Ok(data.get("k\"l").unwrap()), },
Test {
ptr: "/ ",
data,
expected: Ok(data.get(" ").unwrap()), },
Test {
ptr: "/m~0n",
data,
expected: Ok(data.get("m~n").unwrap()), },
Test {
ptr: "/object/bool/unresolvable",
data,
expected: Err(ResolveError::Unreachable { offset: 12 }),
},
Test {
ptr: "/object/not_found",
data,
expected: Err(ResolveError::NotFound { offset: 7 }),
},
]);
}
#[test]
#[cfg(feature = "toml")]
fn resolve_toml() {
use toml::{toml, Value};
let data = &Value::Table(toml! {
"array" = ["bar", "baz"]
"object" = {
"object" = {"baz" = {"qux" = "quux"}},
"strings" = ["zero", "one", "two"],
"bool" = true,
"objects" = [{"field" = "zero"}, {"field" = "one"}, {"field" = "two"}]
}
"" = 0
"a/b" = 1
"c%d" = 2
"e^f" = 3
"g|h" = 4
"i\\j" = 5
"k\"l" = 6
" " = 7
"m~n" = 8
});
Test::all([
Test {
ptr: "",
data,
expected: Ok(data),
},
Test {
ptr: "/array",
data,
expected: Ok(data.get("array").unwrap()), },
Test {
ptr: "/array/0",
data,
expected: Ok(data.get("array").unwrap().get(0).unwrap()), },
Test {
ptr: "/a~1b",
data,
expected: Ok(data.get("a/b").unwrap()), },
Test {
ptr: "/c%d",
data,
expected: Ok(data.get("c%d").unwrap()), },
Test {
ptr: "/e^f",
data,
expected: Ok(data.get("e^f").unwrap()), },
Test {
ptr: "/g|h",
data,
expected: Ok(data.get("g|h").unwrap()), },
Test {
ptr: "/i\\j",
data,
expected: Ok(data.get("i\\j").unwrap()), },
Test {
ptr: "/k\"l",
data,
expected: Ok(data.get("k\"l").unwrap()), },
Test {
ptr: "/ ",
data,
expected: Ok(data.get(" ").unwrap()), },
Test {
ptr: "/m~0n",
data,
expected: Ok(data.get("m~n").unwrap()), },
Test {
ptr: "/object/bool/unresolvable",
data,
expected: Err(ResolveError::Unreachable { offset: 12 }),
},
Test {
ptr: "/object/not_found",
data,
expected: Err(ResolveError::NotFound { offset: 7 }),
},
]);
}
struct Test<'v, V> {
ptr: &'static str,
expected: Result<&'v V, ResolveError>,
data: &'v V,
}
impl<'v, V> Test<'v, V>
where
V: Resolve<Value = V, Error = ResolveError>
+ ResolveMut<Value = V, Error = ResolveError>
+ Clone
+ PartialEq
+ fmt::Display
+ fmt::Debug,
{
fn all(tests: impl IntoIterator<Item = Test<'v, V>>) {
tests.into_iter().enumerate().for_each(|(i, t)| t.run(i));
}
fn run(self, _i: usize) {
_ = self;
let Test {
ptr,
data,
expected,
} = self;
let ptr = Pointer::from_static(ptr);
let mut data = data.clone();
let expected = expected.cloned();
let res = data.resolve(ptr).cloned();
assert_eq!(&res, &expected);
let res = data.resolve_mut(ptr).cloned();
assert_eq!(&res, &expected);
}
}
}