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 /// # #[cfg(feature = "parse")] {
187 /// let formatted_value = "'literal'".parse::<toml_edit::Value>().unwrap();
188 /// let mut arr = toml_edit::Array::new();
189 /// arr.push_formatted(formatted_value);
190 /// # }
191 /// ```
192 pub fn push_formatted(&mut self, v: Value) {
193 self.values.push(Item::Value(v));
194 }
195
196 /// Inserts an element at the given position within the array, applying default formatting to
197 /// it and shifting all values after it to the right.
198 ///
199 /// # Panics
200 ///
201 /// Panics if `index > len`.
202 ///
203 /// # Examples
204 ///
205 /// ```rust
206 /// let mut arr = toml_edit::Array::new();
207 /// arr.push(1);
208 /// arr.push("foo");
209 ///
210 /// arr.insert(0, "start");
211 /// ```
212 pub fn insert<V: Into<Value>>(&mut self, index: usize, v: V) {
213 self.value_op(v.into(), true, |items, value| {
214 items.insert(index, Item::Value(value))
215 })
216 }
217
218 /// Inserts an already formatted value at the given position within the array, shifting all
219 /// values after it to the right.
220 ///
221 /// # Panics
222 ///
223 /// Panics if `index > len`.
224 ///
225 /// # Examples
226 ///
227 /// ```rust
228 /// # #[cfg(feature = "parse")] {
229 /// let mut arr = toml_edit::Array::new();
230 /// arr.push(1);
231 /// arr.push("foo");
232 ///
233 /// let formatted_value = "'start'".parse::<toml_edit::Value>().unwrap();
234 /// arr.insert_formatted(0, formatted_value);
235 /// # }
236 /// ```
237 pub fn insert_formatted(&mut self, index: usize, v: Value) {
238 self.values.insert(index, Item::Value(v))
239 }
240
241 /// Replaces the element at the given position within the array, preserving existing formatting.
242 ///
243 /// # Panics
244 ///
245 /// Panics if `index >= len`.
246 ///
247 /// # Examples
248 ///
249 /// ```rust
250 /// let mut arr = toml_edit::Array::new();
251 /// arr.push(1);
252 /// arr.push("foo");
253 ///
254 /// arr.replace(0, "start");
255 /// ```
256 pub fn replace<V: Into<Value>>(&mut self, index: usize, v: V) -> Value {
257 // Read the existing value's decor and preserve it.
258 let existing_decor = self
259 .get(index)
260 .unwrap_or_else(|| panic!("index {} out of bounds (len = {})", index, self.len()))
261 .decor();
262 let mut value = v.into();
263 *value.decor_mut() = existing_decor.clone();
264 self.replace_formatted(index, value)
265 }
266
267 /// Replaces the element at the given position within the array with an already formatted value.
268 ///
269 /// # Panics
270 ///
271 /// Panics if `index >= len`.
272 ///
273 /// # Examples
274 ///
275 /// ```rust
276 /// # #[cfg(feature = "parse")] {
277 /// let mut arr = toml_edit::Array::new();
278 /// arr.push(1);
279 /// arr.push("foo");
280 ///
281 /// let formatted_value = "'start'".parse::<toml_edit::Value>().unwrap();
282 /// arr.replace_formatted(0, formatted_value);
283 /// # }
284 /// ```
285 pub fn replace_formatted(&mut self, index: usize, v: Value) -> Value {
286 match mem::replace(&mut self.values[index], Item::Value(v)) {
287 Item::Value(old_value) => old_value,
288 x => panic!("non-value item {:?} in an array", x),
289 }
290 }
291
292 /// Removes the value at the given index.
293 ///
294 /// # Examples
295 ///
296 /// ```rust
297 /// let mut arr = toml_edit::Array::new();
298 /// arr.push(1);
299 /// arr.push("foo");
300 ///
301 /// arr.remove(0);
302 /// assert_eq!(arr.len(), 1);
303 /// ```
304 pub fn remove(&mut self, index: usize) -> Value {
305 let removed = self.values.remove(index);
306 match removed {
307 Item::Value(v) => v,
308 x => panic!("non-value item {:?} in an array", x),
309 }
310 }
311
312 /// Retains only the values specified by the `keep` predicate.
313 ///
314 /// In other words, remove all values for which `keep(&value)` returns `false`.
315 ///
316 /// This method operates in place, visiting each element exactly once in the
317 /// original order, and preserves the order of the retained elements.
318 pub fn retain<F>(&mut self, mut keep: F)
319 where
320 F: FnMut(&Value) -> bool,
321 {
322 self.values
323 .retain(|item| item.as_value().map(&mut keep).unwrap_or(false));
324 }
325
326 /// Sorts the slice with a comparator function.
327 ///
328 /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) worst-case.
329 ///
330 /// The comparator function must define a total ordering for the elements in the slice. If
331 /// the ordering is not total, the order of the elements is unspecified. An order is a
332 /// total order if it is (for all `a`, `b` and `c`):
333 ///
334 /// * total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true, and
335 /// * transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
336 ///
337 /// For example, while [`f64`] doesn't implement [`Ord`] because `NaN != NaN`, we can use
338 /// `partial_cmp` as our sort function when we know the slice doesn't contain a `NaN`.
339 #[inline]
340 pub fn sort_by<F>(&mut self, mut compare: F)
341 where
342 F: FnMut(&Value, &Value) -> std::cmp::Ordering,
343 {
344 self.values.sort_by(move |lhs, rhs| {
345 let lhs = lhs.as_value();
346 let rhs = rhs.as_value();
347 match (lhs, rhs) {
348 (None, None) => std::cmp::Ordering::Equal,
349 (Some(_), None) => std::cmp::Ordering::Greater,
350 (None, Some(_)) => std::cmp::Ordering::Less,
351 (Some(lhs), Some(rhs)) => compare(lhs, rhs),
352 }
353 })
354 }
355
356 /// Sorts the array with a key extraction function.
357 ///
358 /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* \* log(*n*))
359 /// worst-case, where the key function is *O*(*m*).
360 #[inline]
361 pub fn sort_by_key<K, F>(&mut self, mut f: F)
362 where
363 F: FnMut(&Value) -> K,
364 K: Ord,
365 {
366 #[allow(clippy::manual_map)] // needed for lifetimes
367 self.values.sort_by_key(move |item| {
368 if let Some(value) = item.as_value() {
369 Some(f(value))
370 } else {
371 None
372 }
373 });
374 }
375
376 fn value_op<T>(
377 &mut self,
378 v: Value,
379 decorate: bool,
380 op: impl FnOnce(&mut Vec<Item>, Value) -> T,
381 ) -> T {
382 let mut value = v;
383 if !self.is_empty() && decorate {
384 value.decorate(" ", "");
385 } else if decorate {
386 value.decorate("", "");
387 }
388 op(&mut self.values, value)
389 }
390}
391
392#[cfg(feature = "display")]
393impl std::fmt::Display for Array {
394 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
395 crate::encode::Encode::encode(self, buf:f, input:None, ("", ""))
396 }
397}
398
399impl<V: Into<Value>> Extend<V> for Array {
400 fn extend<T: IntoIterator<Item = V>>(&mut self, iter: T) {
401 for value: V in iter {
402 self.push_formatted(value.into());
403 }
404 }
405}
406
407impl<V: Into<Value>> FromIterator<V> for Array {
408 fn from_iter<I>(iter: I) -> Self
409 where
410 I: IntoIterator<Item = V>,
411 {
412 let v: impl Iterator = iter.into_iter().map(|a: V| Item::Value(a.into()));
413 Array {
414 values: v.collect(),
415 ..Default::default()
416 }
417 }
418}
419
420impl IntoIterator for Array {
421 type Item = Value;
422 type IntoIter = ArrayIntoIter;
423
424 fn into_iter(self) -> Self::IntoIter {
425 Box::new(
426 self.values
427 .into_iter()
428 .filter(|v: &Item| v.is_value())
429 .map(|v: Item| v.into_value().unwrap()),
430 )
431 }
432}
433
434impl<'s> IntoIterator for &'s Array {
435 type Item = &'s Value;
436 type IntoIter = ArrayIter<'s>;
437
438 fn into_iter(self) -> Self::IntoIter {
439 self.iter()
440 }
441}
442
443fn decorate_array(array: &mut Array) {
444 for (i: usize, value: &mut Value) in arrayimpl Iterator
445 .values
446 .iter_mut()
447 .filter_map(Item::as_value_mut)
448 .enumerate()
449 {
450 // [value1, value2, value3]
451 if i == 0 {
452 value.decorate(DEFAULT_LEADING_VALUE_DECOR.0, DEFAULT_LEADING_VALUE_DECOR.1);
453 } else {
454 value.decorate(DEFAULT_VALUE_DECOR.0, DEFAULT_VALUE_DECOR.1);
455 }
456 }
457 // Since everything is now on the same line, remove trailing commas and whitespace.
458 array.set_trailing_comma(yes:false);
459 array.set_trailing("");
460}
461