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 | /// ``` |
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 |
54 | impl 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 | |
107 | impl 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" )] |
395 | impl std::fmt::Display for Array { |
396 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
397 | crate::encode::encode_array(self, buf:f, input:None, ("" , "" )) |
398 | } |
399 | } |
400 | |
401 | impl<V: Into<Value>> Extend<V> for Array { |
402 | fn extend<T: IntoIterator<Item = V>>(&mut self, iter: T) { |
403 | for value: V in iter { |
404 | self.push_formatted(value.into()); |
405 | } |
406 | } |
407 | } |
408 | |
409 | impl<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: impl Iterator = iter.into_iter().map(|a: V| 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 | |
426 | fn into_iter(self) -> Self::IntoIter { |
427 | Box::new( |
428 | self.values |
429 | .into_iter() |
430 | .filter(|v: &Item| v.is_value()) |
431 | .map(|v: Item| 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 | |
440 | fn into_iter(self) -> Self::IntoIter { |
441 | self.iter() |
442 | } |
443 | } |
444 | |
445 | fn decorate_array(array: &mut Array) { |
446 | for (i: usize, value: &mut Value) in arrayimpl Iterator |
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(yes:false); |
461 | array.set_trailing("" ); |
462 | } |
463 | |