serde_yaml/value/
index.rs

1use crate::mapping::Entry;
2use crate::{mapping, private, Mapping, Value};
3use std::fmt::{self, Debug};
4use std::ops;
5
6/// A type that can be used to index into a `serde_yaml::Value`. See the `get`
7/// and `get_mut` methods of `Value`.
8///
9/// This trait is sealed and cannot be implemented for types outside of
10/// `serde_yaml`.
11pub trait Index: private::Sealed {
12    /// Return None if the key is not already in the sequence or object.
13    #[doc(hidden)]
14    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value>;
15
16    /// Return None if the key is not already in the sequence or object.
17    #[doc(hidden)]
18    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value>;
19
20    /// Panic if sequence index out of bounds. If key is not already in the object,
21    /// insert it with a value of null. Panic if Value is a type that cannot be
22    /// indexed into, except if Value is null then it can be treated as an empty
23    /// object.
24    #[doc(hidden)]
25    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value;
26}
27
28impl Index for usize {
29    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
30        match v.untag_ref() {
31            Value::Sequence(vec) => vec.get(*self),
32            Value::Mapping(vec) => vec.get(&Value::Number((*self).into())),
33            _ => None,
34        }
35    }
36    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
37        match v.untag_mut() {
38            Value::Sequence(vec) => vec.get_mut(*self),
39            Value::Mapping(vec) => vec.get_mut(&Value::Number((*self).into())),
40            _ => None,
41        }
42    }
43    fn index_or_insert<'v>(&self, mut v: &'v mut Value) -> &'v mut Value {
44        loop {
45            match v {
46                Value::Sequence(vec) => {
47                    let len = vec.len();
48                    return vec.get_mut(*self).unwrap_or_else(|| {
49                        panic!(
50                            "cannot access index {} of YAML sequence of length {}",
51                            self, len
52                        )
53                    });
54                }
55                Value::Mapping(map) => {
56                    let n = Value::Number((*self).into());
57                    return map.entry(n).or_insert(Value::Null);
58                }
59                Value::Tagged(tagged) => v = &mut tagged.value,
60                _ => panic!("cannot access index {} of YAML {}", self, Type(v)),
61            }
62        }
63    }
64}
65
66fn index_into_mapping<'v, I>(index: &I, v: &'v Value) -> Option<&'v Value>
67where
68    I: ?Sized + mapping::Index,
69{
70    match v.untag_ref() {
71        Value::Mapping(map) => map.get(index),
72        _ => None,
73    }
74}
75
76fn index_into_mut_mapping<'v, I>(index: &I, v: &'v mut Value) -> Option<&'v mut Value>
77where
78    I: ?Sized + mapping::Index,
79{
80    match v.untag_mut() {
81        Value::Mapping(map) => map.get_mut(index),
82        _ => None,
83    }
84}
85
86fn index_or_insert_mapping<'v, I>(index: &I, mut v: &'v mut Value) -> &'v mut Value
87where
88    I: ?Sized + mapping::Index + ToOwned + Debug,
89    Value: From<I::Owned>,
90{
91    if let Value::Null = *v {
92        *v = Value::Mapping(Mapping::new());
93        return match v {
94            Value::Mapping(map) => match map.entry(index.to_owned().into()) {
95                Entry::Vacant(entry) => entry.insert(Value::Null),
96                Entry::Occupied(_) => unreachable!(),
97            },
98            _ => unreachable!(),
99        };
100    }
101    loop {
102        match v {
103            Value::Mapping(map) => {
104                return map.entry(index.to_owned().into()).or_insert(Value::Null);
105            }
106            Value::Tagged(tagged) => v = &mut tagged.value,
107            _ => panic!("cannot access key {:?} in YAML {}", index, Type(v)),
108        }
109    }
110}
111
112impl Index for Value {
113    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
114        index_into_mapping(self, v)
115    }
116    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
117        index_into_mut_mapping(self, v)
118    }
119    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
120        index_or_insert_mapping(self, v)
121    }
122}
123
124impl Index for str {
125    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
126        index_into_mapping(self, v)
127    }
128    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
129        index_into_mut_mapping(self, v)
130    }
131    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
132        index_or_insert_mapping(self, v)
133    }
134}
135
136impl Index for String {
137    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
138        self.as_str().index_into(v)
139    }
140    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
141        self.as_str().index_into_mut(v)
142    }
143    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
144        self.as_str().index_or_insert(v)
145    }
146}
147
148impl<'a, T> Index for &'a T
149where
150    T: ?Sized + Index,
151{
152    fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
153        (**self).index_into(v)
154    }
155    fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
156        (**self).index_into_mut(v)
157    }
158    fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
159        (**self).index_or_insert(v)
160    }
161}
162
163/// Used in panic messages.
164struct Type<'a>(&'a Value);
165
166impl<'a> fmt::Display for Type<'a> {
167    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
168        match self.0 {
169            Value::Null => formatter.write_str("null"),
170            Value::Bool(_) => formatter.write_str("boolean"),
171            Value::Number(_) => formatter.write_str("number"),
172            Value::String(_) => formatter.write_str("string"),
173            Value::Sequence(_) => formatter.write_str("sequence"),
174            Value::Mapping(_) => formatter.write_str("mapping"),
175            Value::Tagged(_) => unreachable!(),
176        }
177    }
178}
179
180// The usual semantics of Index is to panic on invalid indexing.
181//
182// That said, the usual semantics are for things like `Vec` and `BTreeMap` which
183// have different use cases than Value. If you are working with a Vec, you know
184// that you are working with a Vec and you can get the len of the Vec and make
185// sure your indices are within bounds. The Value use cases are more
186// loosey-goosey. You got some YAML from an endpoint and you want to pull values
187// out of it. Outside of this Index impl, you already have the option of using
188// `value.as_sequence()` and working with the Vec directly, or matching on
189// `Value::Sequence` and getting the Vec directly. The Index impl means you can
190// skip that and index directly into the thing using a concise syntax. You don't
191// have to check the type, you don't have to check the len, it is all about what
192// you expect the Value to look like.
193//
194// Basically the use cases that would be well served by panicking here are
195// better served by using one of the other approaches: `get` and `get_mut`,
196// `as_sequence`, or match. The value of this impl is that it adds a way of
197// working with Value that is not well served by the existing approaches:
198// concise and careless and sometimes that is exactly what you want.
199impl<I> ops::Index<I> for Value
200where
201    I: Index,
202{
203    type Output = Value;
204
205    /// Index into a `serde_yaml::Value` using the syntax `value[0]` or
206    /// `value["k"]`.
207    ///
208    /// Returns `Value::Null` if the type of `self` does not match the type of
209    /// the index, for example if the index is a string and `self` is a sequence
210    /// or a number. Also returns `Value::Null` if the given key does not exist
211    /// in the map or the given index is not within the bounds of the sequence.
212    ///
213    /// For retrieving deeply nested values, you should have a look at the
214    /// `Value::pointer` method.
215    ///
216    /// # Examples
217    ///
218    /// ```
219    /// # use serde_yaml::Value;
220    /// #
221    /// # fn main() -> serde_yaml::Result<()> {
222    /// let data: serde_yaml::Value = serde_yaml::from_str(r#"{ x: { y: [z, zz] } }"#)?;
223    ///
224    /// assert_eq!(data["x"]["y"], serde_yaml::from_str::<Value>(r#"["z", "zz"]"#).unwrap());
225    /// assert_eq!(data["x"]["y"][0], serde_yaml::from_str::<Value>(r#""z""#).unwrap());
226    ///
227    /// assert_eq!(data["a"], serde_yaml::from_str::<Value>(r#"null"#).unwrap()); // returns null for undefined values
228    /// assert_eq!(data["a"]["b"], serde_yaml::from_str::<Value>(r#"null"#).unwrap()); // does not panic
229    /// # Ok(())
230    /// # }
231    /// ```
232    fn index(&self, index: I) -> &Value {
233        static NULL: Value = Value::Null;
234        index.index_into(self).unwrap_or(&NULL)
235    }
236}
237
238impl<I> ops::IndexMut<I> for Value
239where
240    I: Index,
241{
242    /// Write into a `serde_yaml::Value` using the syntax `value[0] = ...` or
243    /// `value["k"] = ...`.
244    ///
245    /// If the index is a number, the value must be a sequence of length bigger
246    /// than the index. Indexing into a value that is not a sequence or a
247    /// sequence that is too small will panic.
248    ///
249    /// If the index is a string, the value must be an object or null which is
250    /// treated like an empty object. If the key is not already present in the
251    /// object, it will be inserted with a value of null. Indexing into a value
252    /// that is neither an object nor null will panic.
253    ///
254    /// # Examples
255    ///
256    /// ```
257    /// # fn main() -> serde_yaml::Result<()> {
258    /// let mut data: serde_yaml::Value = serde_yaml::from_str(r#"{x: 0}"#)?;
259    ///
260    /// // replace an existing key
261    /// data["x"] = serde_yaml::from_str(r#"1"#)?;
262    ///
263    /// // insert a new key
264    /// data["y"] = serde_yaml::from_str(r#"[false, false, false]"#)?;
265    ///
266    /// // replace a value in a sequence
267    /// data["y"][0] = serde_yaml::from_str(r#"true"#)?;
268    ///
269    /// // inserted a deeply nested key
270    /// data["a"]["b"]["c"]["d"] = serde_yaml::from_str(r#"true"#)?;
271    ///
272    /// println!("{:?}", data);
273    /// # Ok(())
274    /// # }
275    /// ```
276    fn index_mut(&mut self, index: I) -> &mut Value {
277        index.index_or_insert(self)
278    }
279}