use crate::error::{Result, WelError};
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Segment {
Key(String),
Index(usize),
Wildcard,
}
impl Segment {
fn parse(s: &str) -> Self {
if s == "*" {
Segment::Wildcard
} else if let Ok(idx) = s.parse::<usize>() {
Segment::Index(idx)
} else {
let unescaped = s.replace("~1", "/").replace("~0", "~");
Segment::Key(unescaped)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct JsonPointer {
segments: Vec<Segment>,
}
impl JsonPointer {
pub fn root() -> Self {
Self {
segments: Vec::new(),
}
}
pub fn parse(s: &str) -> Result<Self> {
if s.is_empty() {
return Ok(Self::root());
}
if !s.starts_with('/') {
return Err(WelError::InvalidPointer(format!(
"JSON pointer must start with '/' or be empty: {s}"
)));
}
let segments = s[1..].split('/').map(Segment::parse).collect();
Ok(Self { segments })
}
pub fn segments(&self) -> &[Segment] {
&self.segments
}
pub fn is_empty(&self) -> bool {
self.segments.is_empty()
}
pub fn has_wildcards(&self) -> bool {
self.segments.iter().any(|s| matches!(s, Segment::Wildcard))
}
pub fn join(&self, other: &JsonPointer) -> JsonPointer {
let mut segments = self.segments.clone();
segments.extend(other.segments.iter().cloned());
JsonPointer { segments }
}
pub fn push(&mut self, segment: Segment) {
self.segments.push(segment);
}
pub fn push_key(&mut self, key: impl Into<String>) {
self.segments.push(Segment::Key(key.into()));
}
pub fn push_index(&mut self, index: usize) {
self.segments.push(Segment::Index(index));
}
pub fn resolve<'a>(&self, value: &'a Value) -> Vec<&'a Value> {
self.resolve_recursive(value, 0)
}
fn resolve_recursive<'a>(&self, value: &'a Value, depth: usize) -> Vec<&'a Value> {
if depth >= self.segments.len() {
return vec![value];
}
match &self.segments[depth] {
Segment::Key(key) => {
if let Some(v) = value.get(key) {
self.resolve_recursive(v, depth + 1)
} else {
vec![]
}
}
Segment::Index(idx) => {
if let Some(v) = value.get(idx) {
self.resolve_recursive(v, depth + 1)
} else {
vec![]
}
}
Segment::Wildcard => {
if let Some(arr) = value.as_array() {
arr.iter()
.flat_map(|v| self.resolve_recursive(v, depth + 1))
.collect()
} else {
vec![]
}
}
}
}
pub fn resolve_mut<'a>(&self, value: &'a mut Value) -> Option<&'a mut Value> {
if self.has_wildcards() {
return None;
}
let mut current = value;
for segment in &self.segments {
current = match segment {
Segment::Key(key) => current.get_mut(key)?,
Segment::Index(idx) => current.get_mut(idx)?,
Segment::Wildcard => return None,
};
}
Some(current)
}
pub fn resolve_with_paths<'a>(&self, value: &'a Value) -> Vec<(JsonPointer, &'a Value)> {
self.resolve_with_paths_recursive(value, 0, JsonPointer::root())
}
fn resolve_with_paths_recursive<'a>(
&self,
value: &'a Value,
depth: usize,
current_path: JsonPointer,
) -> Vec<(JsonPointer, &'a Value)> {
if depth >= self.segments.len() {
return vec![(current_path, value)];
}
match &self.segments[depth] {
Segment::Key(key) => {
if let Some(v) = value.get(key) {
let mut path = current_path;
path.push(Segment::Key(key.clone()));
self.resolve_with_paths_recursive(v, depth + 1, path)
} else {
vec![]
}
}
Segment::Index(idx) => {
if let Some(v) = value.get(idx) {
let mut path = current_path;
path.push(Segment::Index(*idx));
self.resolve_with_paths_recursive(v, depth + 1, path)
} else {
vec![]
}
}
Segment::Wildcard => {
if let Some(arr) = value.as_array() {
arr.iter()
.enumerate()
.flat_map(|(i, v)| {
let mut path = current_path.clone();
path.push(Segment::Index(i));
self.resolve_with_paths_recursive(v, depth + 1, path)
})
.collect()
} else {
vec![]
}
}
}
}
}
impl std::fmt::Display for JsonPointer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.segments.is_empty() {
Ok(())
} else {
write!(f, "/")?;
for (i, segment) in self.segments.iter().enumerate() {
if i > 0 {
write!(f, "/")?;
}
match segment {
Segment::Key(k) => {
write!(f, "{}", k.replace('~', "~0").replace('/', "~1"))?;
}
Segment::Index(idx) => write!(f, "{}", idx)?,
Segment::Wildcard => write!(f, "*")?,
}
}
Ok(())
}
}
}
impl Serialize for JsonPointer {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use std::fmt::Write;
let mut s = String::new();
write!(s, "{}", self).expect("formatting to string failed");
serializer.serialize_str(&s)
}
}
impl<'de> Deserialize<'de> for JsonPointer {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
JsonPointer::parse(&s).map_err(serde::de::Error::custom)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_parse_empty() {
let ptr = JsonPointer::parse("").unwrap();
assert!(ptr.is_empty());
assert_eq!(ptr.to_string(), "");
}
#[test]
fn test_parse_simple() {
let ptr = JsonPointer::parse("/foo/bar").unwrap();
assert_eq!(ptr.segments.len(), 2);
assert_eq!(ptr.segments[0], Segment::Key("foo".to_string()));
assert_eq!(ptr.segments[1], Segment::Key("bar".to_string()));
assert_eq!(ptr.to_string(), "/foo/bar");
}
#[test]
fn test_parse_with_index() {
let ptr = JsonPointer::parse("/items/0/name").unwrap();
assert_eq!(ptr.segments.len(), 3);
assert_eq!(ptr.segments[0], Segment::Key("items".to_string()));
assert_eq!(ptr.segments[1], Segment::Index(0));
assert_eq!(ptr.segments[2], Segment::Key("name".to_string()));
}
#[test]
fn test_parse_with_wildcard() {
let ptr = JsonPointer::parse("/items/*/name").unwrap();
assert!(ptr.has_wildcards());
assert_eq!(ptr.segments[1], Segment::Wildcard);
}
#[test]
fn test_parse_escaped() {
let ptr = JsonPointer::parse("/foo~1bar/baz~0qux").unwrap();
assert_eq!(ptr.segments[0], Segment::Key("foo/bar".to_string()));
assert_eq!(ptr.segments[1], Segment::Key("baz~qux".to_string()));
}
#[test]
fn test_parse_invalid() {
assert!(JsonPointer::parse("foo/bar").is_err());
}
#[test]
fn test_resolve_simple() {
let value = json!({
"foo": {
"bar": 42
}
});
let ptr = JsonPointer::parse("/foo/bar").unwrap();
let results = ptr.resolve(&value);
assert_eq!(results.len(), 1);
assert_eq!(results[0], &json!(42));
}
#[test]
fn test_resolve_array_index() {
let value = json!({
"items": ["a", "b", "c"]
});
let ptr = JsonPointer::parse("/items/1").unwrap();
let results = ptr.resolve(&value);
assert_eq!(results.len(), 1);
assert_eq!(results[0], &json!("b"));
}
#[test]
fn test_resolve_wildcard() {
let value = json!({
"items": [
{"name": "alice"},
{"name": "bob"},
{"name": "charlie"}
]
});
let ptr = JsonPointer::parse("/items/*/name").unwrap();
let results = ptr.resolve(&value);
assert_eq!(results.len(), 3);
assert_eq!(results[0], &json!("alice"));
assert_eq!(results[1], &json!("bob"));
assert_eq!(results[2], &json!("charlie"));
}
#[test]
fn test_resolve_with_paths() {
let value = json!({
"items": [
{"name": "alice"},
{"name": "bob"}
]
});
let ptr = JsonPointer::parse("/items/*/name").unwrap();
let results = ptr.resolve_with_paths(&value);
assert_eq!(results.len(), 2);
assert_eq!(results[0].0.to_string(), "/items/0/name");
assert_eq!(results[0].1, &json!("alice"));
assert_eq!(results[1].0.to_string(), "/items/1/name");
assert_eq!(results[1].1, &json!("bob"));
}
#[test]
fn test_join() {
let base = JsonPointer::parse("/foo").unwrap();
let rel = JsonPointer::parse("/bar/baz").unwrap();
let joined = base.join(&rel);
assert_eq!(joined.to_string(), "/foo/bar/baz");
}
#[test]
fn test_serde_roundtrip() {
let ptr = JsonPointer::parse("/foo/*/bar").unwrap();
let json = serde_json::to_string(&ptr).unwrap();
assert_eq!(json, "\"/foo/*/bar\"");
let parsed: JsonPointer = serde_json::from_str(&json).unwrap();
assert_eq!(ptr, parsed);
}
}