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}