Skip to main content

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}