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!(resolved.unwrap(), &json!({"payload": {"type": "object"}}));
91 }
92
93 #[test]
94 fn test_resolve_array_index() {
95 let doc = json!({
96 "items": [{"name": "a"}, {"name": "b"}]
97 });
98 let resolved = resolve_json_pointer(&doc, "#/items/1/name");
99 assert_eq!(resolved, Some(&json!("b")));
100 }
101
102 #[test]
103 fn test_escape_sequences() {
104 let doc = json!({
105 "a~b/c": "value"
106 });
107 let resolved = resolve_json_pointer(&doc, "#/a~0b~1c");
108 assert_eq!(resolved, Some(&json!("value")));
109 }
110}