Skip to main content

serde_yaml/value/
index.rs

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