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