1use crate::diagnostics::{Diagnostic, DiagnosticCode};
23use crate::pointer_errors::{
24 build_array_index_out_of_bounds_error, build_invalid_array_index_error,
25 build_key_not_found_error, build_type_mismatch_error,
26};
27use serde_json::Value;
28
29#[derive(Debug, Clone, PartialEq)]
30pub struct JsonPointer {
31 tokens: Vec<String>,
32}
33
34impl JsonPointer {
35 pub fn new(path: &str) -> Result<Self, Diagnostic> {
36 if path.is_empty() {
37 return Ok(JsonPointer { tokens: vec![] });
38 }
39
40 if !path.starts_with('/') {
41 return Err(Diagnostic::fatal(
42 DiagnosticCode::InvalidPointerSyntax,
43 format!(
44 "I couldn't parse the path '{}': Path must start with '/'",
45 path
46 ),
47 ));
48 }
49
50 let tokens = path[1..]
51 .split('/')
52 .map(|token| token.replace("~1", "/").replace("~0", "~"))
53 .collect();
54
55 Ok(JsonPointer { tokens })
56 }
57
58 pub fn get_mut<'a>(&self, value: &'a mut Value) -> Result<&'a mut Value, Diagnostic> {
63 let mut current = value;
64
65 for (token_index, token) in self.tokens.iter().enumerate() {
66 match current {
67 Value::Object(obj) => {
68 if obj.contains_key(token) {
69 current = obj.get_mut(token).unwrap();
70 } else {
71 let keys: Vec<String> = obj.keys().cloned().collect();
72 let key_refs: Vec<&str> = keys.iter().map(|s| s.as_str()).collect();
73 return Err(build_key_not_found_error(
74 &self.tokens,
75 token_index,
76 token,
77 &key_refs,
78 ));
79 }
80 }
81 Value::Array(arr) => {
82 let arr_len = arr.len();
83 let index = token.parse::<usize>().map_err(|_| {
84 build_invalid_array_index_error(&self.tokens, token_index, token, arr)
85 })?;
86 if index < arr_len {
87 current = &mut arr[index];
88 } else {
89 return Err(build_array_index_out_of_bounds_error(
90 &self.tokens,
91 token_index,
92 index,
93 arr_len,
94 arr,
95 ));
96 }
97 }
98 _ => {
99 return Err(build_type_mismatch_error(
100 &self.tokens,
101 token_index,
102 token,
103 current,
104 ));
105 }
106 }
107 }
108
109 Ok(current)
110 }
111
112 fn parent(&self) -> JsonPointer {
117 JsonPointer {
118 tokens: self.tokens[..self.tokens.len() - 1].to_vec(),
119 }
120 }
121
122 pub fn set(&self, value: &mut Value, new_value: Value) -> Result<(), Diagnostic> {
123 if self.tokens.is_empty() {
124 *value = new_value;
125 return Ok(());
126 }
127
128 let last_token = &self.tokens[self.tokens.len() - 1];
129 let parent = self.parent().get_mut(value)?;
130
131 match parent {
132 Value::Object(obj) => {
133 obj.insert(last_token.clone(), new_value);
134 }
135 Value::Array(arr) => {
136 let index = last_token.parse::<usize>().map_err(|_| {
137 Diagnostic::fatal(
138 DiagnosticCode::InvalidArrayIndex,
139 format!("I couldn't parse '{}' as an array index", last_token),
140 )
141 })?;
142
143 if index == arr.len() {
144 arr.push(new_value);
145 } else if index < arr.len() {
146 arr[index] = new_value;
147 } else {
148 return Err(Diagnostic::fatal(
149 DiagnosticCode::PathNotFound,
150 format!(
151 "I couldn't set index {} (array length is {})",
152 index,
153 arr.len()
154 ),
155 ));
156 }
157 }
158 _ => {
159 return Err(Diagnostic::fatal(
160 DiagnosticCode::TypeMismatch,
161 format!(
162 "I can't set property '{}' on {}",
163 last_token,
164 parent.type_name()
165 ),
166 ));
167 }
168 }
169
170 Ok(())
171 }
172
173 pub fn remove(&self, value: &mut Value) -> Result<Value, Diagnostic> {
174 if self.tokens.is_empty() {
175 return Err(Diagnostic::fatal(
176 DiagnosticCode::InvalidPointerSyntax,
177 "I can't remove the root value".to_string(),
178 ));
179 }
180
181 let last_token = &self.tokens[self.tokens.len() - 1];
182 let parent = self.parent().get_mut(value)?;
183
184 match parent {
185 Value::Object(obj) => obj.remove(last_token).ok_or_else(|| {
186 Diagnostic::fatal(
187 DiagnosticCode::PathNotFound,
188 format!("I couldn't find the key '{}' to remove", last_token),
189 )
190 }),
191 Value::Array(arr) => {
192 let index = last_token.parse::<usize>().map_err(|_| {
193 Diagnostic::fatal(
194 DiagnosticCode::InvalidArrayIndex,
195 format!("I couldn't parse '{}' as an array index", last_token),
196 )
197 })?;
198
199 if index < arr.len() {
200 Ok(arr.remove(index))
201 } else {
202 Err(Diagnostic::fatal(
203 DiagnosticCode::PathNotFound,
204 format!(
205 "I couldn't remove index {} (array length is {})",
206 index,
207 arr.len()
208 ),
209 ))
210 }
211 }
212 _ => Err(Diagnostic::fatal(
213 DiagnosticCode::TypeMismatch,
214 format!(
215 "I can't remove property '{}' from {}",
216 last_token,
217 parent.type_name()
218 ),
219 )),
220 }
221 }
222
223 pub fn to_string(&self) -> String {
224 if self.tokens.is_empty() {
225 return "".to_string();
226 }
227
228 let escaped_tokens: Vec<String> = self
229 .tokens
230 .iter()
231 .map(|token| token.replace("~", "~0").replace("/", "~1"))
232 .collect();
233
234 format!("/{}", escaped_tokens.join("/"))
235 }
236}
237
238trait ValueTypeExt {
239 fn type_name(&self) -> &'static str;
240}
241
242impl ValueTypeExt for Value {
243 fn type_name(&self) -> &'static str {
244 match self {
245 Value::Null => "null",
246 Value::Bool(_) => "boolean",
247 Value::Number(_) => "number",
248 Value::String(_) => "string",
249 Value::Array(_) => "array",
250 Value::Object(_) => "object",
251 }
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258 use serde_json::json;
259
260 #[test]
261 fn test_empty_pointer() {
262 let pointer = JsonPointer::new("").unwrap();
263 let mut value = json!({"foo": "bar"});
264 assert_eq!(pointer.get_mut(&mut value).unwrap(), &json!({"foo": "bar"}));
265 }
266
267 #[test]
268 fn test_simple_object_access() {
269 let pointer = JsonPointer::new("/foo").unwrap();
270 let mut value = json!({"foo": "bar"});
271 assert_eq!(pointer.get_mut(&mut value).unwrap(), &json!("bar"));
272 }
273
274 #[test]
275 fn test_nested_object_access() {
276 let pointer = JsonPointer::new("/foo/bar").unwrap();
277 let mut value = json!({"foo": {"bar": "baz"}});
278 assert_eq!(pointer.get_mut(&mut value).unwrap(), &json!("baz"));
279 }
280
281 #[test]
282 fn test_array_access() {
283 let pointer = JsonPointer::new("/items/0").unwrap();
284 let mut value = json!({"items": ["first", "second"]});
285 assert_eq!(pointer.get_mut(&mut value).unwrap(), &json!("first"));
286 }
287
288 #[test]
289 fn test_escape_sequences() {
290 let pointer = JsonPointer::new("/foo~1bar").unwrap();
291 let mut value = json!({"foo/bar": "baz"});
292 assert_eq!(pointer.get_mut(&mut value).unwrap(), &json!("baz"));
293
294 let pointer = JsonPointer::new("/foo~0bar").unwrap();
295 let mut value = json!({"foo~bar": "baz"});
296 assert_eq!(pointer.get_mut(&mut value).unwrap(), &json!("baz"));
297 }
298
299 #[test]
300 fn test_set_object() {
301 let pointer = JsonPointer::new("/foo").unwrap();
302 let mut value = json!({"foo": "bar"});
303 pointer.set(&mut value, json!("new_value")).unwrap();
304 assert_eq!(value, json!({"foo": "new_value"}));
305 }
306
307 #[test]
308 fn test_set_array_append() {
309 let pointer = JsonPointer::new("/items/2").unwrap();
310 let mut value = json!({"items": ["first", "second"]});
311 pointer.set(&mut value, json!("third")).unwrap();
312 assert_eq!(value, json!({"items": ["first", "second", "third"]}));
313 }
314
315 #[test]
316 fn test_remove_object() {
317 let pointer = JsonPointer::new("/foo").unwrap();
318 let mut value = json!({"foo": "bar", "baz": "qux"});
319 let removed = pointer.remove(&mut value).unwrap();
320 assert_eq!(removed, json!("bar"));
321 assert_eq!(value, json!({"baz": "qux"}));
322 }
323
324 #[test]
325 fn test_remove_array() {
326 let pointer = JsonPointer::new("/items/0").unwrap();
327 let mut value = json!({"items": ["first", "second", "third"]});
328 let removed = pointer.remove(&mut value).unwrap();
329 assert_eq!(removed, json!("first"));
330 assert_eq!(value, json!({"items": ["second", "third"]}));
331 }
332}