use serde_json::Value;
pub fn resolve_pointer<'a>(value: &'a Value, pointer: &str) -> Option<&'a Value> {
if pointer.is_empty() {
return Some(value);
}
if !pointer.starts_with('/') {
return None;
}
let mut current = value;
for token in pointer[1..].split('/') {
let unescaped = unescape_token(token)?;
current = match current {
Value::Object(map) => map.get(&unescaped)?,
Value::Array(arr) => {
let idx = parse_array_index(&unescaped)?;
arr.get(idx)?
}
_ => return None,
};
}
Some(current)
}
fn unescape_token(token: &str) -> Option<String> {
let mut output = String::with_capacity(token.len());
let mut chars = token.chars();
while let Some(ch) = chars.next() {
if ch != '~' {
output.push(ch);
continue;
}
match chars.next() {
Some('0') => output.push('~'),
Some('1') => output.push('/'),
_ => return None,
}
}
Some(output)
}
fn parse_array_index(token: &str) -> Option<usize> {
if token == "0" {
return Some(0);
}
if token.is_empty() || token.starts_with('0') || !token.chars().all(|ch| ch.is_ascii_digit()) {
return None;
}
token.parse().ok()
}
#[allow(dead_code)]
pub fn escape_token(s: &str) -> String {
s.replace('~', "~0").replace('/', "~1")
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_empty_pointer() {
let doc = json!({"foo": 1});
assert_eq!(resolve_pointer(&doc, ""), Some(&doc));
}
#[test]
fn test_simple_path() {
let doc = json!({"foo": {"bar": 42}});
assert_eq!(resolve_pointer(&doc, "/foo"), Some(&json!({"bar": 42})));
assert_eq!(resolve_pointer(&doc, "/foo/bar"), Some(&json!(42)));
}
#[test]
fn test_array_index() {
let doc = json!({"items": [10, 20, 30]});
assert_eq!(resolve_pointer(&doc, "/items/0"), Some(&json!(10)));
assert_eq!(resolve_pointer(&doc, "/items/2"), Some(&json!(30)));
assert_eq!(resolve_pointer(&doc, "/items/3"), None);
}
#[test]
fn test_escaped_tokens() {
let doc = json!({"a/b": {"c~d": 1}});
assert_eq!(resolve_pointer(&doc, "/a~1b/c~0d"), Some(&json!(1)));
}
#[test]
fn test_invalid_escape_sequence() {
let doc = json!({"a~2b": 1, "a~b": 2});
assert_eq!(resolve_pointer(&doc, "/a~2b"), None);
assert_eq!(resolve_pointer(&doc, "/a~"), None);
}
#[test]
fn test_invalid_pointer() {
let doc = json!({"foo": 1});
assert_eq!(resolve_pointer(&doc, "foo"), None);
assert_eq!(resolve_pointer(&doc, "/bar"), None);
}
#[test]
fn test_nested_arrays() {
let doc = json!({"matrix": [[1, 2], [3, 4]]});
assert_eq!(resolve_pointer(&doc, "/matrix/0/1"), Some(&json!(2)));
assert_eq!(resolve_pointer(&doc, "/matrix/1/0"), Some(&json!(3)));
}
#[test]
fn test_invalid_array_indexes() {
let doc = json!({"items": ["zero", "one", "two"]});
assert_eq!(resolve_pointer(&doc, "/items/01"), None);
assert_eq!(resolve_pointer(&doc, "/items/+1"), None);
}
#[test]
fn test_escape_token() {
assert_eq!(escape_token("a/b"), "a~1b");
assert_eq!(escape_token("a~b"), "a~0b");
assert_eq!(escape_token("a/b~c"), "a~1b~0c");
}
}