1pub fn resolve_json_pointer<'a>(
2 root: &'a serde_json::Value,
3 pointer: &str,
4) -> Option<&'a serde_json::Value> {
5 if pointer.is_empty() || pointer == "#" {
6 return Some(root);
7 }
8
9 let path = if pointer.starts_with("#/") {
10 &pointer[2..]
11 } else if pointer.starts_with('/') {
12 &pointer[1..]
13 } else {
14 return None;
15 };
16
17 if path.is_empty() {
18 return Some(root);
19 }
20
21 let segments = parse_pointer_segments(path);
22 let mut current = root;
23
24 for segment in segments {
25 match current {
26 serde_json::Value::Object(map) => {
27 current = map.get(&segment)?;
28 }
29 serde_json::Value::Array(arr) => {
30 let index: usize = segment.parse().ok()?;
31 current = arr.get(index)?;
32 }
33 _ => return None,
34 }
35 }
36
37 Some(current)
38}
39
40fn parse_pointer_segments(path: &str) -> Vec<String> {
41 let mut segments = Vec::new();
42 let mut current = String::new();
43 let mut chars = path.chars();
44
45 while let Some(c) = chars.next() {
46 if c == '/' {
47 segments.push(std::mem::take(&mut current));
48 } else if c == '~' {
49 match chars.next() {
50 Some('0') => current.push('~'),
51 Some('1') => current.push('/'),
52 _ => {}
53 }
54 } else {
55 current.push(c);
56 }
57 }
58 segments.push(current);
59
60 segments
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use serde_json::json;
67
68 #[test]
69 fn test_resolve_root() {
70 let doc = json!({"a": 1});
71 assert_eq!(resolve_json_pointer(&doc, ""), Some(&doc));
72 assert_eq!(resolve_json_pointer(&doc, "#"), Some(&doc));
73 assert_eq!(resolve_json_pointer(&doc, "#/"), Some(&doc));
74 }
75
76 #[test]
77 fn test_resolve_nested() {
78 let doc = json!({
79 "components": {
80 "messages": {
81 "OrderPlaced": {
82 "payload": {"type": "object"}
83 }
84 }
85 }
86 });
87
88 let resolved = resolve_json_pointer(&doc, "#/components/messages/OrderPlaced");
89 assert!(resolved.is_some());
90 assert_eq!(
91 resolved.unwrap(),
92 &json!({"payload": {"type": "object"}})
93 );
94 }
95
96 #[test]
97 fn test_resolve_array_index() {
98 let doc = json!({
99 "items": [{"name": "a"}, {"name": "b"}]
100 });
101 let resolved = resolve_json_pointer(&doc, "#/items/1/name");
102 assert_eq!(resolved, Some(&json!("b")));
103 }
104
105 #[test]
106 fn test_escape_sequences() {
107 let doc = json!({
108 "a~b/c": "value"
109 });
110 let resolved = resolve_json_pointer(&doc, "#/a~0b~1c");
111 assert_eq!(resolved, Some(&json!("value")));
112 }
113}