toml_edit/
array.rs

1use std::iter::FromIterator;
2use std::mem;
3
4use crate::repr::Decor;
5use crate::value::{DEFAULT_LEADING_VALUE_DECOR, DEFAULT_VALUE_DECOR};
6use crate::{Item, RawString, Value};
7
8/// Type representing a TOML array,
9/// payload of the `Value::Array` variant's value
10#[derive(Debug, Default, Clone)]
11pub struct Array {
12    // `trailing` represents whitespaces, newlines
13    // and comments in an empty array or after the trailing comma
14    trailing: RawString,
15    trailing_comma: bool,
16    // prefix before `[` and suffix after `]`
17    decor: Decor,
18    pub(crate) span: Option<std::ops::Range<usize>>,
19    // always Vec<Item::Value>
20    pub(crate) values: Vec<Item>,
21}
22
23/// An owned iterator type over `Table`'s key/value pairs.
24pub type ArrayIntoIter = Box<dyn Iterator<Item = Value>>;
25/// An iterator type over `Array`'s values.
26pub type ArrayIter<'a> = Box<dyn Iterator<Item = &'a Value> + 'a>;
27/// An iterator type over `Array`'s values.
28pub type ArrayIterMut<'a> = Box<dyn Iterator<Item = &'a mut Value> + 'a>;
29
30/// Constructors
31///
32/// See also `FromIterator`
33impl Array {
34    /// Create an empty `Array`
35    ///
36    /// # Examples
37    ///
38    /// ```rust
39    /// let mut arr = toml_edit::Array::new();
40    /// ```
41    pub fn new() -> Self {
42        Default::default()
43    }
44
45    pub(crate) fn with_vec(values: Vec<Item>) -> Self {
46        Self {
47            values,
48            ..Default::default()
49        }
50    }
51}
52
53/// Formatting
54impl Array {
55    /// Auto formats the array.
56    pub fn fmt(&mut self) {
57        decorate_array(self);
58    }
59
60    /// Set whether the array will use a trailing comma
61    pub fn set_trailing_comma(&mut self, yes: bool) {
62        self.trailing_comma = yes;
63    }
64
65    /// Whether the array will use a trailing comma
66    pub fn trailing_comma(&self) -> bool {
67        self.trailing_comma
68    }
69
70    /// Set whitespace after last element
71    pub fn set_trailing(&mut self, trailing: impl Into<RawString>) {
72        self.trailing = trailing.into();
73    }
74
75    /// Whitespace after last element
76    pub fn trailing(&self) -> &RawString {
77        &self.trailing
78    }
79
80    /// Returns the surrounding whitespace
81    pub fn decor_mut(&mut self) -> &mut Decor {
82        &mut self.decor
83    }
84
85    /// Returns the surrounding whitespace
86    pub fn decor(&self) -> &Decor {
87        &self.decor
88    }
89
90    /// Returns the location within the original document
91    pub(crate) fn span(&self) -> Option<std::ops::Range<usize>> {
92        self.span.clone()
93    }
94
95    pub(crate) fn despan(&mut self, input: &str) {
96        self.span = None;
97        self.decor.despan(input);
98        self.trailing.despan(input);
99        for value in &mut self.values {
100            value.despan(input);
101        }
102    }
103}
104
105impl Array {
106    /// Returns an iterator over all values.
107    pub fn iter(&self) -> ArrayIter<'_> {
108        Box::new(self.values.iter().filter_map(Item::as_value))
109    }
110
111    /// Returns an iterator over all values.
112    pub fn iter_mut(&mut self) -> ArrayIterMut<'_> {
113        Box::new(self.values.iter_mut().filter_map(Item::as_value_mut))
114    }
115
116    /// Returns the length of the underlying Vec.
117    ///
118    /// In some rare cases, placeholder elements will exist.  For a more accurate count, call
119    /// `a.iter().count()`
120    ///
121    /// # Examples
122    ///
123    /// ```rust
124    /// let mut arr = toml_edit::Array::new();
125    /// arr.push(1);
126    /// arr.push("foo");
127    /// assert_eq!(arr.len(), 2);
128    /// ```
129    pub fn len(&self) -> usize {
130        self.values.len()
131    }
132
133    /// Return true iff `self.len() == 0`.
134    ///
135    /// # Examples
136    ///
137    /// ```rust
138    /// let mut arr = toml_edit::Array::new();
139    /// assert!(arr.is_empty());
140    ///
141    /// arr.push(1);
142    /// arr.push("foo");
143    /// assert!(! arr.is_empty());
144    /// ```
145    pub fn is_empty(&self) -> bool {
146        self.len() == 0
147    }
148
149    /// Clears the array, removing all values. Keeps the allocated memory for reuse.
150    pub fn clear(&mut self) {
151        self.values.clear()
152    }
153
154    /// Returns a reference to the value at the given index, or `None` if the index is out of
155    /// bounds.
156    pub fn get(&self, index: usize) -> Option<&Value> {
157        self.values.get(index).and_then(Item::as_value)
158    }
159
160    /// Returns a reference to the value at the given index, or `None` if the index is out of
161    /// bounds.
162    pub fn get_mut(&mut self, index: usize) -> Option<&mut Value> {
163        self.values.get_mut(index).and_then(Item::as_value_mut)
164    }
165
166    /// Appends a new value to the end of the array, applying default formatting to it.
167    ///
168    /// # Examples
169    ///
170    /// ```rust
171    /// let mut arr = toml_edit::Array::new();
172    /// arr.push(1);
173    /// arr.push("foo");
174    /// ```
175    pub fn push<V: Into<Value>>(&mut self, v: V) {
176        self.value_op(v.into(), true, |items, value| {
177            items.push(Item::Value(value))
178        })
179    }
180
181    /// Appends a new, already formatted value to the end of the array.
182    ///
183    /// # Examples
184    ///
185    /// ```rust
186    /// let formatted_value = "'literal'".parse::<toml_edit::Value>().unwrap();
187    /// let mut arr = toml_edit::Array::new();
188    /// arr.push_formatted(formatted_value);
189    /// ```
190    pub fn push_formatted(&mut self, v: Value) {
191        self.values.push(Item::Value(v));
192    }
193
194    /// Inserts an element at the given position within the array, applying default formatting to
195    /// it and shifting all values after it to the right.
196    ///
197    /// # Panics
198    ///
199    /// Panics if `index > len`.
200    ///
201    /// # Examples
202    ///
203    /// ```rust
204    /// let mut arr = toml_edit::Array::new();
205    /// arr.push(1);
206    /// arr.push("foo");
207    ///
208    /// arr.insert(0, "start");
209    /// ```
210    pub fn insert<V: Into<Value>>(&mut self, index: usize, v: V) {
211        self.value_op(v.into(), true, |items, value| {
212            items.insert(index, Item::Value(value))
213        })
214    }
215
216    /// Inserts an already formatted value at the given position within the array, shifting all
217    /// values after it to the right.
218    ///
219    /// # Panics
220    ///
221    /// Panics if `index > len`.
222    ///
223    /// # Examples
224    ///
225    /// ```rust
226    /// let mut arr = toml_edit::Array::new();
227    /// arr.push(1);
228    /// arr.push("foo");
229    ///
230    /// let formatted_value = "'start'".parse::<toml_edit::Value>().unwrap();
231    /// arr.insert_formatted(0, formatted_value);
232    /// ```
233    pub fn insert_formatted(&mut self, index: usize, v: Value) {
234        self.values.insert(index, Item::Value(v))
235    }
236
237    /// Replaces the element at the given position within the array, preserving existing formatting.
238    ///
239    /// # Panics
240    ///
241    /// Panics if `index >= len`.
242    ///
243    /// # Examples
244    ///
245    /// ```rust
246    /// let mut arr = toml_edit::Array::new();
247    /// arr.push(1);
248    /// arr.push("foo");
249    ///
250    /// arr.replace(0, "start");
251    /// ```
252    pub fn replace<V: Into<Value>>(&mut self, index: usize, v: V) -> Value {
253        // Read the existing value's decor and preserve it.
254        let existing_decor = self
255            .get(index)
256            .unwrap_or_else(|| panic!("index {} out of bounds (len = {})", index, self.len()))
257            .decor();
258        let mut value = v.into();
259        *value.decor_mut() = existing_decor.clone();
260        self.replace_formatted(index, value)
261    }
262
263    /// Replaces the element at the given position within the array with an already formatted value.
264    ///
265    /// # Panics
266    ///
267    /// Panics if `index >= len`.
268    ///
269    /// # Examples
270    ///
271    /// ```rust
272    /// let mut arr = toml_edit::Array::new();
273    /// arr.push(1);
274    /// arr.push("foo");
275    ///
276    /// let formatted_value = "'start'".parse::<toml_edit::Value>().unwrap();
277    /// arr.replace_formatted(0, formatted_value);
278    /// ```
279    pub fn replace_formatted(&mut self, index: usize, v: Value) -> Value {
280        match mem::replace(&mut self.values[index], Item::Value(v)) {
281            Item::Value(old_value) => old_value,
282            x => panic!("non-value item {:?} in an array", x),
283        }
284    }
285
286    /// Removes the value at the given index.
287    ///
288    /// # Examples
289    ///
290    /// ```rust
291    /// let mut arr = toml_edit::Array::new();
292    /// arr.push(1);
293    /// arr.push("foo");
294    ///
295    /// arr.remove(0);
296    /// assert_eq!(arr.len(), 1);
297    /// ```
298    pub fn remove(&mut self, index: usize) -> Value {
299        let removed = self.values.remove(index);
300        match removed {
301            Item::Value(v) => v,
302            x => panic!("non-value item {:?} in an array", x),
303        }
304    }
305
306    /// Retains only the values specified by the `keep` predicate.
307    ///
308    /// In other words, remove all values for which `keep(&value)` returns `false`.
309    ///
310    /// This method operates in place, visiting each element exactly once in the
311    /// original order, and preserves the order of the retained elements.
312    pub fn retain<F>(&mut self, mut keep: F)
313    where
314        F: FnMut(&Value) -> bool,
315    {
316        self.values
317            .retain(|item| item.as_value().map(&mut keep).unwrap_or(false));
318    }
319
320    fn value_op<T>(
321        &mut self,
322        v: Value,
323        decorate: bool,
324        op: impl FnOnce(&mut Vec<Item>, Value) -> T,
325    ) -> T {
326        let mut value = v;
327        if !self.is_empty() && decorate {
328            value.decorate(" ", "");
329        } else if decorate {
330            value.decorate("", "");
331        }
332        op(&mut self.values, value)
333    }
334}
335
336impl std::fmt::Display for Array {
337    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
338        crate::encode::Encode::encode(self, f, None, ("", ""))
339    }
340}
341
342impl<V: Into<Value>> Extend<V> for Array {
343    fn extend<T: IntoIterator<Item = V>>(&mut self, iter: T) {
344        for value in iter {
345            self.push_formatted(value.into());
346        }
347    }
348}
349
350impl<V: Into<Value>> FromIterator<V> for Array {
351    fn from_iter<I>(iter: I) -> Self
352    where
353        I: IntoIterator<Item = V>,
354    {
355        let v = iter.into_iter().map(|a| Item::Value(a.into()));
356        Array {
357            values: v.collect(),
358            ..Default::default()
359        }
360    }
361}
362
363impl IntoIterator for Array {
364    type Item = Value;
365    type IntoIter = ArrayIntoIter;
366
367    fn into_iter(self) -> Self::IntoIter {
368        Box::new(
369            self.values
370                .into_iter()
371                .filter(|v| v.is_value())
372                .map(|v| v.into_value().unwrap()),
373        )
374    }
375}
376
377impl<'s> IntoIterator for &'s Array {
378    type Item = &'s Value;
379    type IntoIter = ArrayIter<'s>;
380
381    fn into_iter(self) -> Self::IntoIter {
382        self.iter()
383    }
384}
385
386fn decorate_array(array: &mut Array) {
387    for (i, value) in array
388        .values
389        .iter_mut()
390        .filter_map(Item::as_value_mut)
391        .enumerate()
392    {
393        // [value1, value2, value3]
394        if i == 0 {
395            value.decorate(DEFAULT_LEADING_VALUE_DECOR.0, DEFAULT_LEADING_VALUE_DECOR.1);
396        } else {
397            value.decorate(DEFAULT_VALUE_DECOR.0, DEFAULT_VALUE_DECOR.1);
398        }
399    }
400    // Since everything is now on the same line, remove trailing commas and whitespace.
401    array.set_trailing_comma(false);
402    array.set_trailing("");
403}