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