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}