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