jsonschema_schema/schema/
navigate.rs1use super::{Schema, SchemaValue};
2
3pub fn ref_name(ref_str: &str) -> &str {
5 ref_str.rsplit('/').next().unwrap_or(ref_str)
6}
7
8pub fn resolve_ref<'a>(schema: &'a Schema, root: &'a Schema) -> &'a Schema {
13 if let Some(ref ref_str) = schema.ref_
14 && let Some(path) = ref_str.strip_prefix("#/")
15 {
16 let Ok(root_value) = serde_json::to_value(root) else {
18 return schema;
19 };
20 let mut current = &root_value;
21 for segment in path.split('/') {
22 let decoded = segment.replace("~1", "/").replace("~0", "~");
23 match current.get(&decoded) {
24 Some(next) => current = next,
25 None => return schema,
26 }
27 }
28 let _ = current;
33 return schema;
34 }
35 schema
36}
37
38pub fn navigate_pointer<'a>(
47 schema: &'a SchemaValue,
48 root: &'a SchemaValue,
49 pointer: &str,
50) -> Result<&'a SchemaValue, String> {
51 let path = pointer.strip_prefix('/').unwrap_or(pointer);
52 if path.is_empty() {
53 return Ok(schema);
54 }
55
56 let mut current = resolve_schema_value_ref(schema, root);
57 let mut segments = path.split('/').peekable();
58
59 while let Some(segment) = segments.next() {
60 let decoded = segment.replace("~1", "/").replace("~0", "~");
61 current = resolve_schema_value_ref(current, root);
62
63 let Some(schema) = current.as_schema() else {
64 return Err(format!(
65 "cannot resolve segment '{decoded}' in pointer '{pointer}'"
66 ));
67 };
68
69 if is_map_keyword(&decoded) {
71 let key_segment = segments
72 .next()
73 .ok_or_else(|| format!("expected key after '{decoded}' in pointer '{pointer}'"))?;
74 let key = key_segment.replace("~1", "/").replace("~0", "~");
75 if let Some(entry) = schema.get_map_entry(&decoded, &key) {
76 current = entry;
77 continue;
78 }
79 return Err(format!(
80 "cannot resolve segment '{key}' in '{decoded}' in pointer '{pointer}'"
81 ));
82 }
83
84 if is_array_keyword(&decoded) {
86 let idx_segment = segments.next().ok_or_else(|| {
87 format!("expected index after '{decoded}' in pointer '{pointer}'")
88 })?;
89 let idx: usize = idx_segment.parse().map_err(|_| {
90 format!("expected numeric index after '{decoded}', got '{idx_segment}'")
91 })?;
92 if let Some(entry) = schema.get_array_entry(&decoded, idx) {
93 current = entry;
94 continue;
95 }
96 return Err(format!(
97 "index {idx} out of bounds in '{decoded}' in pointer '{pointer}'"
98 ));
99 }
100
101 if let Some(sv) = schema.get_keyword(&decoded) {
103 current = sv;
104 continue;
105 }
106
107 if let Some(sv) = schema.get_map_entry_by_pointer_segment(&decoded) {
110 current = sv;
111 continue;
112 }
113
114 if let Ok(idx) = decoded.parse::<usize>() {
116 let found = ["allOf", "anyOf", "oneOf", "prefixItems"]
117 .iter()
118 .find_map(|kw| schema.get_array_entry(kw, idx));
119 if let Some(entry) = found {
120 current = entry;
121 continue;
122 }
123 }
124
125 return Err(format!(
126 "cannot resolve segment '{decoded}' in pointer '{pointer}'"
127 ));
128 }
129
130 Ok(resolve_schema_value_ref(current, root))
131}
132
133fn is_map_keyword(segment: &str) -> bool {
135 matches!(
136 segment,
137 "properties" | "patternProperties" | "$defs" | "dependentSchemas"
138 )
139}
140
141fn is_array_keyword(segment: &str) -> bool {
143 matches!(segment, "allOf" | "anyOf" | "oneOf" | "prefixItems")
144}
145
146fn resolve_schema_value_ref<'a>(sv: &'a SchemaValue, root: &'a SchemaValue) -> &'a SchemaValue {
148 let Some(schema) = sv.as_schema() else {
149 return sv;
150 };
151 if let Some(ref ref_str) = schema.ref_
152 && let Some(path) = ref_str.strip_prefix("#/")
153 {
154 let mut current = root;
155 let mut segments = path.split('/').peekable();
156 while let Some(segment) = segments.next() {
157 let decoded = segment.replace("~1", "/").replace("~0", "~");
158 let Some(inner) = current.as_schema() else {
159 return sv;
160 };
161
162 if is_map_keyword(&decoded) {
164 let Some(key_segment) = segments.next() else {
165 return sv;
166 };
167 let key = key_segment.replace("~1", "/").replace("~0", "~");
168 match inner.get_map_entry(&decoded, &key) {
169 Some(n) => current = n,
170 None => return sv,
171 }
172 continue;
173 }
174
175 if is_array_keyword(&decoded) {
177 let Some(idx_segment) = segments.next() else {
178 return sv;
179 };
180 let Ok(idx) = idx_segment.parse::<usize>() else {
181 return sv;
182 };
183 match inner.get_array_entry(&decoded, idx) {
184 Some(n) => current = n,
185 None => return sv,
186 }
187 continue;
188 }
189
190 if let Some(n) = inner.get_keyword(&decoded) {
192 current = n;
193 continue;
194 }
195
196 if let Some(n) = inner.get_map_entry_by_pointer_segment(&decoded) {
198 current = n;
199 continue;
200 }
201
202 return sv;
203 }
204 return current;
205 }
206 sv
207}