• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::iter::FromIterator;
2 use std::mem;
3 
4 use crate::repr::Decor;
5 use crate::value::{DEFAULT_LEADING_VALUE_DECOR, DEFAULT_VALUE_DECOR};
6 use 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)]
11 pub 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.
24 pub type ArrayIntoIter = Box<dyn Iterator<Item = Value>>;
25 /// An iterator type over `Array`'s values.
26 pub type ArrayIter<'a> = Box<dyn Iterator<Item = &'a Value> + 'a>;
27 /// An iterator type over `Array`'s values.
28 pub type ArrayIterMut<'a> = Box<dyn Iterator<Item = &'a mut Value> + 'a>;
29 
30 /// Constructors
31 ///
32 /// See also `FromIterator`
33 impl Array {
34     /// Create an empty `Array`
35     ///
36     /// # Examples
37     ///
38     /// ```rust
39     /// let mut arr = toml_edit::Array::new();
40     /// ```
new() -> Self41     pub fn new() -> Self {
42         Default::default()
43     }
44 
with_vec(values: Vec<Item>) -> Self45     pub(crate) fn with_vec(values: Vec<Item>) -> Self {
46         Self {
47             values,
48             ..Default::default()
49         }
50     }
51 }
52 
53 /// Formatting
54 impl Array {
55     /// Auto formats the array.
fmt(&mut self)56     pub fn fmt(&mut self) {
57         decorate_array(self);
58     }
59 
60     /// Set whether the array will use a trailing comma
set_trailing_comma(&mut self, yes: bool)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
trailing_comma(&self) -> bool66     pub fn trailing_comma(&self) -> bool {
67         self.trailing_comma
68     }
69 
70     /// Set whitespace after last element
set_trailing(&mut self, trailing: impl Into<RawString>)71     pub fn set_trailing(&mut self, trailing: impl Into<RawString>) {
72         self.trailing = trailing.into();
73     }
74 
75     /// Whitespace after last element
trailing(&self) -> &RawString76     pub fn trailing(&self) -> &RawString {
77         &self.trailing
78     }
79 
80     /// Returns the surrounding whitespace
decor_mut(&mut self) -> &mut Decor81     pub fn decor_mut(&mut self) -> &mut Decor {
82         &mut self.decor
83     }
84 
85     /// Returns the surrounding whitespace
decor(&self) -> &Decor86     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].
span(&self) -> Option<std::ops::Range<usize>>93     pub fn span(&self) -> Option<std::ops::Range<usize>> {
94         self.span.clone()
95     }
96 
despan(&mut self, input: &str)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 
107 impl Array {
108     /// Returns an iterator over all values.
iter(&self) -> ArrayIter<'_>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.
iter_mut(&mut self) -> ArrayIterMut<'_>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     /// ```
len(&self) -> usize131     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     /// ```
is_empty(&self) -> bool147     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.
clear(&mut self)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.
get(&self, index: usize) -> Option<&Value>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.
get_mut(&mut self, index: usize) -> Option<&mut Value>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     /// ```
push<V: Into<Value>>(&mut self, v: V)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     /// ```
push_formatted(&mut self, v: Value)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     /// ```
insert<V: Into<Value>>(&mut self, index: usize, v: V)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     /// ```
insert_formatted(&mut self, index: usize, v: Value)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     /// ```
replace<V: Into<Value>>(&mut self, index: usize, v: V) -> Value258     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     /// ```
replace_formatted(&mut self, index: usize, v: Value) -> Value287     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 {:?} in an array", x),
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     /// ```
remove(&mut self, index: usize) -> Value306     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 {:?} in an array", x),
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.
retain<F>(&mut self, mut keep: F) where F: FnMut(&Value) -> bool,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]
sort_by<F>(&mut self, mut compare: F) where F: FnMut(&Value, &Value) -> std::cmp::Ordering,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]
sort_by_key<K, F>(&mut self, mut f: F) where F: FnMut(&Value) -> K, K: Ord,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 
value_op<T>( &mut self, v: Value, decorate: bool, op: impl FnOnce(&mut Vec<Item>, Value) -> T, ) -> T378     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")]
395 impl std::fmt::Display for Array {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result396     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
397         crate::encode::encode_array(self, f, None, ("", ""))
398     }
399 }
400 
401 impl<V: Into<Value>> Extend<V> for Array {
extend<T: IntoIterator<Item = V>>(&mut self, iter: T)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 
409 impl<V: Into<Value>> FromIterator<V> for Array {
from_iter<I>(iter: I) -> Self where I: IntoIterator<Item = V>,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 
422 impl IntoIterator for Array {
423     type Item = Value;
424     type IntoIter = ArrayIntoIter;
425 
into_iter(self) -> Self::IntoIter426     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 
436 impl<'s> IntoIterator for &'s Array {
437     type Item = &'s Value;
438     type IntoIter = ArrayIter<'s>;
439 
into_iter(self) -> Self::IntoIter440     fn into_iter(self) -> Self::IntoIter {
441         self.iter()
442     }
443 }
444 
decorate_array(array: &mut Array)445 fn 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 }
463