platform_value/
pointer.rs

1use crate::{Value, ValueMapHelper};
2use std::mem;
3
4fn parse_index(s: &str) -> Option<usize> {
5    if s.starts_with('+') || (s.starts_with('0') && s.len() != 1) {
6        return None;
7    }
8    s.parse().ok()
9}
10
11impl Value {
12    /// Looks up a value by a Platform Value Pointer.
13    ///
14    /// Platform Value Pointer defines a string syntax for identifying a specific value
15    /// within a Platform Value document.
16    ///
17    /// A Pointer is a Unicode string with the reference tokens separated by `/`.
18    /// Inside tokens `/` is replaced by `~1` and `~` is replaced by `~0`. The
19    /// addressed value is returned and if there is no such value `None` is
20    /// returned.
21    ///
22    /// For more information read [RFC6901](https://tools.ietf.org/html/rfc6901).
23    ///
24    /// # Examples
25    ///
26    /// ```
27    /// # use platform_value::platform_value;
28    /// #
29    /// let data = platform_value!({
30    ///     "x": {
31    ///         "y": ["z", "zz"]
32    ///     }
33    /// });
34    ///
35    /// assert_eq!(data.pointer("/x/y/1").unwrap(), &platform_value!("zz"));
36    /// assert_eq!(data.pointer("/a/b/c"), None);
37    /// ```
38    pub fn pointer(&self, pointer: &str) -> Option<&Value> {
39        if pointer.is_empty() {
40            return Some(self);
41        }
42        if !pointer.starts_with('/') {
43            return None;
44        }
45        pointer
46            .split('/')
47            .skip(1)
48            .map(|x| x.replace("~1", "/").replace("~0", "~"))
49            .try_fold(self, |target, token| match target {
50                Value::Map(map) => map.get_optional_key(&token),
51                Value::Array(list) => parse_index(&token).and_then(|x| list.get(x)),
52                _ => None,
53            })
54    }
55
56    /// Looks up a value by a Platform Value Pointer and returns a mutable reference to
57    /// that value.
58    ///
59    /// Platform Value Pointer defines a string syntax for identifying a specific value
60    /// within a Platform Value document.
61    ///
62    /// A Pointer is a Unicode string with the reference tokens separated by `/`.
63    /// Inside tokens `/` is replaced by `~1` and `~` is replaced by `~0`. The
64    /// addressed value is returned and if there is no such value `None` is
65    /// returned.
66    ///
67    /// For more information read [RFC6901](https://tools.ietf.org/html/rfc6901).
68    ///
69    /// # Example of Use
70    ///
71    /// ```
72    /// use platform_value::Value;
73    ///
74    /// use platform_value::platform_value;
75    /// let mut value: Value = platform_value!({"x": 1.0, "y": 2.0});
76    ///
77    /// // Check value using read-only pointer
78    /// assert_eq!(value.pointer("/x"), Some(&1.0.into()));
79    /// // Change value with direct assignment
80    /// *value.pointer_mut("/x").unwrap() = 1.5.into();
81    /// // Check that new value was written
82    /// assert_eq!(value.pointer("/x"), Some(&1.5.into()));
83    /// // Or change the value only if it exists
84    /// value.pointer_mut("/x").map(|v| *v = 1.5.into());
85    ///
86    /// // "Steal" ownership of a value. Can replace with any valid Value.
87    /// let old_x = value.pointer_mut("/x").map(Value::take).unwrap();
88    /// assert_eq!(old_x, 1.5);
89    /// assert_eq!(value.pointer("/x").unwrap(), &Value::Null);
90    /// ```
91    pub fn pointer_mut(&mut self, pointer: &str) -> Option<&mut Value> {
92        if pointer.is_empty() {
93            return Some(self);
94        }
95        if !pointer.starts_with('/') {
96            return None;
97        }
98        pointer
99            .split('/')
100            .skip(1)
101            .map(|x| x.replace("~1", "/").replace("~0", "~"))
102            .try_fold(self, |target, token| match target {
103                Value::Map(map) => map.get_optional_key_mut(&token),
104                Value::Array(list) => parse_index(&token).and_then(move |x| list.get_mut(x)),
105                _ => None,
106            })
107    }
108
109    /// Takes the value out of the `Value`, leaving a `Null` in its place.
110    ///
111    /// ```
112    /// # use platform_value::platform_value;
113    /// #
114    /// let mut v = platform_value!({ "x": "y" });
115    /// assert_eq!(v["x"].take(), platform_value!("y"));
116    /// assert_eq!(v, platform_value!({ "x": null }));
117    /// ```
118    pub fn take(&mut self) -> Value {
119        mem::replace(self, Value::Null)
120    }
121}