immutable_json/pointer.rs
1use crate::api::Number::{Decimal, Integer};
2use crate::api::{Number, Value};
3use crate::array::Array;
4use crate::error::Error;
5use crate::object::Object;
6use crate::util::{push_back, remove_first, remove_last};
7use imbl::{Vector, vector};
8use std::cmp::Ordering;
9use std::cmp::Ordering::{Equal, Greater, Less};
10use std::fmt::{Display, Formatter};
11use std::iter::zip;
12use std::str::FromStr;
13use take_until::TakeUntilExt;
14
15/// Represents a JSON pointer.
16#[derive(Clone, Eq, PartialEq, Hash, Debug)]
17pub struct JsonPointer {
18 path: Vector<String>,
19}
20
21impl Default for JsonPointer {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl Display for JsonPointer {
28 /// Convert a JSON pointer to a string.
29 /// ```rust
30 /// # use immutable_json::pointer::JsonPointer;
31 /// # use std::str::FromStr;
32 /// # fn main() {
33 ///let p = "/a/b~1c/~0d";
34 ///
35 ///assert_eq!(p, JsonPointer::from_str(p).unwrap().to_string())
36 /// # }
37 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
38 write!(
39 f,
40 "{}",
41 self.path.iter().map(|segment| escape(segment)).fold(
42 "".to_string(),
43 |mut p, segment| {
44 p.push('/');
45 p.push_str(&segment);
46 p
47 }
48 )
49 )
50 }
51}
52
53impl FromStr for JsonPointer {
54 type Err = Error;
55
56 /// Create a JSON pointer from a string.
57 fn from_str(s: &str) -> Result<Self, Self::Err> {
58 if !s.starts_with("/") {
59 Err(Error::JsonPointer(s.to_string()))
60 } else {
61 Ok(Self {
62 path: s
63 .split("/")
64 .filter(|segment| !segment.is_empty())
65 .map(unescape)
66 .fold(Vector::new(), |v, segment| {
67 push_back(&v, segment.to_string())
68 }),
69 })
70 }
71 }
72}
73
74impl Ord for JsonPointer {
75 /// The comparison takes into account array indexes, which are compared numerically.
76 /// ```rust
77 /// # use immutable_json::pointer::JsonPointer;
78 /// # use std::str::FromStr;
79 /// # fn main() {
80 ///assert!(JsonPointer::from_str("/a").unwrap() == JsonPointer::from_str("/a").unwrap());
81 ///assert!(JsonPointer::from_str("/a/b").unwrap() < JsonPointer::from_str("/a/c").unwrap());
82 ///assert!(JsonPointer::from_str("/a/b/0").unwrap() < JsonPointer::from_str("/a/b/1").unwrap());
83 ///assert!(JsonPointer::from_str("/a/b/10").unwrap() > JsonPointer::from_str("/a/b/2").unwrap());
84 ///assert!(JsonPointer::from_str("/a/b/-").unwrap() > JsonPointer::from_str("/a/b/2").unwrap());
85 ///assert!(JsonPointer::from_str("/a/b/1").unwrap() < JsonPointer::from_str("/a/b/-").unwrap())
86 /// # }
87 /// ```
88 fn cmp(&self, other: &Self) -> Ordering {
89 match zip(self.path.iter(), other.path.iter())
90 .map(|(s1, s2)| Self::cmp_segment(s1, s2))
91 .take_until(|cmp| *cmp != Equal)
92 .last()
93 .unwrap_or(Equal)
94 {
95 Less => Less,
96 Equal => self.path.len().cmp(&other.path.len()),
97 Greater => Greater,
98 }
99 }
100}
101
102impl PartialOrd for JsonPointer {
103 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
104 Some(self.cmp(other))
105 }
106}
107
108impl JsonPointer {
109 /// Add a value to a JSON object or array at the location specified by the JSON pointer.
110 /// ```rust
111 /// # use immutable_json::api::Number::Integer;
112 /// # use immutable_json::api::Value;
113 /// # use immutable_json::error::Error;
114 /// # use immutable_json::object::Object;
115 /// # use immutable_json::pointer::JsonPointer;
116 /// # use std::str::FromStr;
117 /// # fn main() -> Result<(), Error>{
118 ///let data = r#"
119 /// [
120 /// "string",
121 /// {"test": "test"},
122 /// ["string"]
123 /// ]"#;
124 ///let array = Value::from_str(data)?.as_array().unwrap();
125 ///
126 ///assert_eq!(array.insert_string(0, "string2").ok().map(|a| Value::Array(a)),
127 /// JsonPointer::from_str("/0")
128 /// .ok()
129 /// .and_then(|p| {
130 /// p.add(&Value::Array(array.clone()), &Value::String("string2".to_string()))
131 /// }));
132 ///assert_eq!(None,
133 /// JsonPointer::from_str("/4")
134 /// .ok()
135 /// .and_then(|p| {
136 /// p.add(&Value::Array(array.clone()), &Value::String("string2".to_string()))
137 /// }));
138 ///assert_eq!(array.insert_integer(3, 0).ok().map(|a| Value::Array(a)),
139 /// JsonPointer::from_str("/-")
140 /// .ok()
141 /// .and_then(|p| p.add(&Value::Array(array.clone()), &Value::Number(Integer(0)))));
142 ///assert_eq!(array.set_object(
143 /// 1,
144 /// &Object::new().add_string("test", "test").add_string("test2", "test2")
145 /// ).ok().map(|a| Value::Array(a)),
146 /// JsonPointer::from_str("/1/test2")
147 /// .ok()
148 /// .and_then(|p| {
149 /// p.add(&Value::Array(array.clone()), &Value::String("test2".to_string()))
150 /// }));
151 /// # Ok(())
152 /// # }
153 /// ```
154 pub fn add(&self, target: &Value, value: &Value) -> Option<Value> {
155 self.modify(target, |v, key| match v {
156 Value::Array(a) => usize::from_str(key)
157 .ok()
158 .and_then(|i| a.insert(i, value).ok().map(Value::Array)),
159 Value::Object(o) => Some(Value::Object(o.add(key, value))),
160 _ => None,
161 })
162 }
163
164 /// Add a JSON array to a JSON object or array at the location specified by the JSON pointer.
165 pub fn add_array(pointer: &str, target: &Value, value: &Array) -> Option<Value> {
166 Self::add_pointer(pointer, target, &Value::Array(value.clone()))
167 }
168
169 /// Add a Boolean value to a JSON object or array at the location specified by the JSON pointer.
170 pub fn add_bool(pointer: &str, target: &Value, value: bool) -> Option<Value> {
171 Self::add_pointer(pointer, target, &Value::Bool(value))
172 }
173
174 /// Add a decimal value to a JSON object or array at the location specified by the JSON pointer.
175 pub fn add_decimal(pointer: &str, target: &Value, value: f64) -> Option<Value> {
176 Self::add_pointer(pointer, target, &Value::Number(Decimal(value)))
177 }
178
179 /// Add an integer value to a JSON object or array at the location specified by the JSON
180 /// pointer.
181 pub fn add_integer(pointer: &str, target: &Value, value: i128) -> Option<Value> {
182 Self::add_pointer(pointer, target, &Value::Number(Integer(value)))
183 }
184
185 /// Add a number to a JSON object or array at the location specified by the JSON pointer.
186 pub fn add_number(pointer: &str, target: &Value, value: Number) -> Option<Value> {
187 Self::add_pointer(pointer, target, &Value::Number(value))
188 }
189
190 /// Add a JSON object to a JSON object or array at the location specified by the JSON pointer.
191 pub fn add_object(pointer: &str, target: &Value, value: &Object) -> Option<Value> {
192 Self::add_pointer(pointer, target, &Value::Object(value.clone()))
193 }
194
195 fn add_pointer(pointer: &str, target: &Value, value: &Value) -> Option<Value> {
196 Self::from_str(pointer)
197 .ok()
198 .and_then(|p| p.add(target, value))
199 }
200
201 /// Add a string value to a JSON object or array at the location specified by the JSON pointer.
202 pub fn add_string(pointer: &str, target: &Value, value: &str) -> Option<Value> {
203 Self::add_pointer(pointer, target, &Value::String(value.to_string()))
204 }
205
206 /// If the pointer refers to a value in an array, the index within the array is returned.
207 pub fn array_index(&self) -> Option<usize> {
208 self.path.last().and_then(|last| usize::from_str(last).ok())
209 }
210
211 fn as_integer(&self) -> Option<usize> {
212 usize::from_str(&self.path[0]).ok()
213 }
214
215 /// Returns a JSON pointer with an extra path segment.
216 pub fn child(&self, segment: &str) -> Self {
217 Self {
218 path: push_back(&self.path, segment.to_string()),
219 }
220 }
221
222 fn cmp_segment(s1: &str, s2: &str) -> Ordering {
223 usize::from_str(s1)
224 .ok()
225 .and_then(|n1| usize::from_str(s2).ok().map(|n2| n1.cmp(&n2)))
226 .or_else(|| {
227 if s2 == "-" {
228 usize::from_str(s1).ok().map(|_| Less)
229 } else {
230 None
231 }
232 })
233 .or_else(|| {
234 if s1 == "-" {
235 usize::from_str(s2).ok().map(|_| Greater)
236 } else {
237 None
238 }
239 })
240 .unwrap_or_else(|| s1.cmp(s2))
241 }
242
243 /// Get a value from a JSON object or array through a JSON pointer.
244 /// ```rust
245 /// # use immutable_json::api::Value;
246 /// # use immutable_json::error::Error;
247 /// # use immutable_json::pointer::JsonPointer;
248 /// # use std::str::FromStr;
249 /// # fn main() -> Result<(), Error>{
250 ///let data = r#"
251 /// {
252 /// "string": "string",
253 /// "int": 43,
254 /// "float": 5.8,
255 /// "boolean": true,
256 /// "object": {"test": "test"},
257 /// "array": [
258 /// "string",
259 /// 1,
260 /// 3.0,
261 /// false,
262 /// {"test": "test"},
263 /// [1]
264 /// ],
265 /// "es/cape": true
266 /// }"#;
267 ///let object = Value::from_str(data)?;
268 ///
269 ///assert_eq!(Some(Value::String("string".to_string())),
270 /// JsonPointer::from_str("/string").ok().and_then(|p| p.get(&object)));
271 ///assert_eq!(Some(Value::String("test".to_string())),
272 /// JsonPointer::from_str("/object/test").ok().and_then(|p| p.get(&object)));
273 ///assert_eq!(None, JsonPointer::from_str("/object/test2").ok().and_then(|p| p.get(&object)));
274 ///assert_eq!(None, JsonPointer::from_str("/object2/test").ok().and_then(|p| p.get(&object)));
275 ///assert_eq!(Some(Value::String("test".to_string())),
276 /// JsonPointer::from_str("/array/4/test").ok().and_then(|p| p.get(&object)));
277 ///assert_eq!(None, JsonPointer::from_str("/array/3/test").ok().and_then(|p| p.get(&object)));
278 ///assert_eq!(None, JsonPointer::from_str("/array2/4/test").ok().and_then(|p| p.get(&object)));
279 ///assert_eq!(Some(Value::Bool(true)),
280 /// JsonPointer::from_str("/es~1cape").ok().and_then(|p| p.get(&object)));
281 ///assert_eq!(Some(object.clone()), JsonPointer::from_str("/").ok().and_then(|p| p.get(&object)));
282 /// # Ok(())
283 /// # }
284 /// ```
285 pub fn get(&self, target: &Value) -> Option<Value> {
286 match target {
287 Value::Array(a) => self.get_from_array(a),
288 Value::Object(o) => self.get_from_object(o),
289 _ => None,
290 }
291 }
292
293 /// Get a value from a JSON object or array through a JSON pointer if it is an array.
294 /// Otherwise, `None` is returned.
295 pub fn get_array(pointer: &str, target: &Value) -> Option<Array> {
296 Self::get_pointer(pointer, target).and_then(|v| v.as_array())
297 }
298
299 /// Get a value from a JSON object or array through a JSON pointer if it is a Boolean.
300 /// Otherwise, `None` is returned.
301 pub fn get_bool(pointer: &str, target: &Value) -> Option<bool> {
302 Self::get_pointer(pointer, target).and_then(|v| v.as_bool())
303 }
304
305 /// Get a value from a JSON object or array through a JSON pointer if it is a decimal.
306 /// Otherwise, `None` is returned.
307 pub fn get_decimal(pointer: &str, target: &Value) -> Option<f64> {
308 Self::get_pointer(pointer, target).and_then(|v| v.as_decimal())
309 }
310
311 fn get_from_array(&self, target: &Array) -> Option<Value> {
312 if self.path.is_empty() {
313 Some(Value::Array(target.clone()))
314 } else {
315 self.as_integer()
316 .and_then(|i| target.get(i).ok())
317 .and_then(|v| self.next_level_or(&v))
318 }
319 }
320
321 fn get_from_object(&self, target: &Object) -> Option<Value> {
322 if self.path.is_empty() {
323 Some(Value::Object(target.clone()))
324 } else {
325 target
326 .get(&self.path[0])
327 .and_then(|v| self.next_level_or(v))
328 }
329 }
330
331 /// Get a value from a JSON object or array through a JSON pointer if it is an integer.
332 /// Otherwise, `None` is returned.
333 pub fn get_integer(pointer: &str, target: &Value) -> Option<i128> {
334 Self::get_pointer(pointer, target).and_then(|v| v.as_integer())
335 }
336
337 /// Get a value from a JSON object or array through a JSON pointer if it is a number.
338 /// Otherwise, `None` is returned.
339 pub fn get_number(pointer: &str, target: &Value) -> Option<Number> {
340 Self::get_pointer(pointer, target).and_then(|v| v.as_number())
341 }
342
343 /// Get a value from a JSON object or array through a JSON pointer if it is an object.
344 /// Otherwise, `None` is returned.
345 pub fn get_object(pointer: &str, target: &Value) -> Option<Object> {
346 Self::get_pointer(pointer, target).and_then(|v| v.as_object())
347 }
348
349 fn get_pointer(pointer: &str, target: &Value) -> Option<Value> {
350 Self::from_str(pointer).ok().and_then(|p| p.get(target))
351 }
352
353 /// Get a value from a JSON object or array through a JSON pointer if it is a string.
354 /// Otherwise, `None` is returned.
355 pub fn get_string(pointer: &str, target: &Value) -> Option<String> {
356 Self::get_pointer(pointer, target).and_then(|v| v.as_string())
357 }
358
359 fn modify<F>(&self, target: &Value, update: F) -> Option<Value>
360 where
361 F: Fn(&Value, &str) -> Option<Value>,
362 {
363 match target {
364 Value::Array(a) => self.modify_array(a, update).map(Value::Array),
365 Value::Object(o) => self.modify_object(o, update).map(Value::Object),
366 _ => None,
367 }
368 }
369
370 fn modify_array<F>(&self, target: &Array, update: F) -> Option<Array>
371 where
372 F: Fn(&Value, &str) -> Option<Value>,
373 {
374 if self.path.is_empty() {
375 None
376 } else if self.path.len() == 1 {
377 self.update_index(target)
378 .and_then(|i| update(&Value::Array(target.clone()), &i.to_string()))
379 .and_then(|v| v.as_array())
380 } else {
381 self.next()
382 .and_then(|p| {
383 self.as_integer()
384 .and_then(|i| target.get(i).ok())
385 .and_then(|v| p.modify(&v, update))
386 })
387 .and_then(|v| self.as_integer().and_then(|i| target.set(i, &v).ok()))
388 }
389 }
390
391 fn modify_object<F>(&self, target: &Object, update: F) -> Option<Object>
392 where
393 F: Fn(&Value, &str) -> Option<Value>,
394 {
395 if self.path.is_empty() {
396 None
397 } else if self.path.len() == 1 {
398 update(&Value::Object(target.clone()), &self.path[0]).and_then(|v| v.as_object())
399 } else {
400 self.next()
401 .and_then(|p| target.get(&self.path[0]).and_then(|v| p.modify(v, update)))
402 .map(|v| target.add(&self.path[0], &v))
403 }
404 }
405
406 /// Creates a JSON pointer that refers to the root.
407 pub fn new() -> Self {
408 Self { path: vector!() }
409 }
410
411 fn next(&self) -> Option<JsonPointer> {
412 if self.path.len() <= 1 {
413 None
414 } else {
415 Some(Self {
416 path: remove_first(&self.path),
417 })
418 }
419 }
420
421 fn next_level_or(&self, target: &Value) -> Option<Value> {
422 match self.next() {
423 Some(n) => match target {
424 Value::Array(a) => n.get_from_array(a),
425 Value::Object(o) => n.get_from_object(o),
426 _ => None,
427 },
428 None => Some(target.clone()),
429 }
430 }
431
432 /// Returns a JSON pointer that refers to the parent.
433 pub fn parent(&self) -> Self {
434 Self {
435 path: remove_last(&self.path),
436 }
437 }
438
439 /// Remove a value in a JSON object or array at the location specified by the JSON pointer.
440 /// ```rust
441 /// # use immutable_json::api::Number;
442 /// # use immutable_json::api::Value;
443 /// # use immutable_json::error::Error;
444 /// # use immutable_json::object::Object;
445 /// # use immutable_json::pointer::JsonPointer;
446 /// # use std::str::FromStr;
447 /// # fn main() -> Result<(), Error>{
448 ///let data = r#"
449 /// [
450 /// "string",
451 /// {"test": "test", "test2": "test2"},
452 /// ["string"]
453 /// ]"#;
454 ///let array = Value::from_str(data)?.as_array().unwrap();
455 ///
456 ///assert_eq!(array.remove(0).ok().map(|a| Value::Array(a)),
457 /// JsonPointer::from_str("/0")
458 /// .ok()
459 /// .and_then(|p| p.remove(&Value::Array(array.clone()))));
460 ///assert_eq!(None,
461 /// JsonPointer::from_str("/4")
462 /// .ok()
463 /// .and_then(|p| p.remove(&Value::Array(array.clone()))));
464 ///assert_eq!(array.set_object(1, &Object::new().add_string("test", "test"))
465 /// .ok().map(|a| Value::Array(a)),
466 /// JsonPointer::from_str("/1/test2")
467 /// .ok()
468 /// .and_then(|p| p.remove(&Value::Array(array.clone()))));
469 /// # Ok(())
470 /// # }
471 /// ```
472 pub fn remove(&self, target: &Value) -> Option<Value> {
473 self.modify(target, |v, key| match v {
474 Value::Array(a) => usize::from_str(key)
475 .ok()
476 .and_then(|i| a.remove(i).ok().map(Value::Array)),
477 Value::Object(o) => Some(Value::Object(o.remove(key))),
478 _ => None,
479 })
480 }
481
482 /// Set a value in a JSON object or array at the location specified by the JSON pointer.
483 /// ```rust
484 /// # use immutable_json::api::Number;
485 /// # use immutable_json::api::Value;
486 /// # use immutable_json::error::Error;
487 /// # use immutable_json::object::Object;
488 /// # use immutable_json::pointer::JsonPointer;
489 /// # use std::str::FromStr;
490 /// # fn main() -> Result<(), Error>{
491 ///let data = r#"
492 /// [
493 /// "string",
494 /// {"test": "test"},
495 /// ["string"]
496 /// ]"#;
497 ///let array = Value::from_str(data)?.as_array().unwrap();
498 ///
499 ///assert_eq!(array.set_string(0, "string2").ok().map(|a| Value::Array(a)),
500 /// JsonPointer::from_str("/0")
501 /// .ok()
502 /// .and_then(|p| {
503 /// p.set(&Value::Array(array.clone()), &Value::String("string2".to_string()))
504 /// }));
505 ///assert_eq!(None,
506 /// JsonPointer::from_str("/4")
507 /// .ok()
508 /// .and_then(|p| {
509 /// p.set(&Value::Array(array.clone()), &Value::String("string2".to_string()))
510 /// }));
511 ///assert_eq!(array.set_object(1, &Object::new().add_string("test", "test2"))
512 /// .ok().map(|a| Value::Array(a)),
513 /// JsonPointer::from_str("/1/test")
514 /// .ok()
515 /// .and_then(|p| {
516 /// p.set(&Value::Array(array.clone()), &Value::String("test2".to_string()))
517 /// }));
518 /// # Ok(())
519 /// # }
520 /// ```
521 pub fn set(&self, target: &Value, value: &Value) -> Option<Value> {
522 self.modify(target, |v, key| match v {
523 Value::Array(a) => usize::from_str(key)
524 .ok()
525 .and_then(|i| a.set(i, value).ok().map(Value::Array)),
526 Value::Object(o) => Some(Value::Object(o.add(key, value))),
527 _ => None,
528 })
529 }
530
531 /// Set an array in a JSON object or array at the location specified by the JSON pointer.
532 pub fn set_array(pointer: &str, target: &Value, value: &Array) -> Option<Value> {
533 Self::set_pointer(pointer, target, &Value::Array(value.clone()))
534 }
535
536 /// Set a Boolean value in a JSON object or array at the location specified by the JSON pointer.
537 pub fn set_bool(pointer: &str, target: &Value, value: bool) -> Option<Value> {
538 Self::set_pointer(pointer, target, &Value::Bool(value))
539 }
540
541 /// Set a decimal value in a JSON object or array at the location specified by the JSON pointer.
542 pub fn set_decimal(pointer: &str, target: &Value, value: f64) -> Option<Value> {
543 Self::set_pointer(pointer, target, &Value::Number(Decimal(value)))
544 }
545
546 /// Set an integer value in a JSON object or array at the location specified by the JSON
547 /// pointer.
548 pub fn set_integer(pointer: &str, target: &Value, value: i128) -> Option<Value> {
549 Self::set_pointer(pointer, target, &Value::Number(Integer(value)))
550 }
551
552 /// Set a number in a JSON object or array at the location specified by the JSON pointer.
553 pub fn set_number(pointer: &str, target: &Value, value: Number) -> Option<Value> {
554 Self::set_pointer(pointer, target, &Value::Number(value))
555 }
556
557 /// Set a JSON object in a JSON object or array at the location specified by the JSON pointer.
558 pub fn set_object(pointer: &str, target: &Value, value: &Object) -> Option<Value> {
559 Self::set_pointer(pointer, target, &Value::Object(value.clone()))
560 }
561
562 fn set_pointer(pointer: &str, target: &Value, value: &Value) -> Option<Value> {
563 Self::from_str(pointer)
564 .ok()
565 .and_then(|p| p.set(target, value))
566 }
567
568 /// Set a string value in a JSON object or array at the location specified by the JSON pointer.
569 pub fn set_string(pointer: &str, target: &Value, value: &str) -> Option<Value> {
570 Self::set_pointer(pointer, target, &Value::String(value.to_string()))
571 }
572
573 fn update_index(&self, array: &Array) -> Option<usize> {
574 if self.path[0] == "-" {
575 Some(array.len())
576 } else {
577 self.as_integer().filter(|i| *i <= array.len())
578 }
579 }
580}
581
582fn escape(s: &str) -> String {
583 s.replace("~", "~0").replace("/", "~1")
584}
585
586fn unescape(s: &str) -> String {
587 s.replace("~1", "/").replace("~0", "~")
588}