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 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}