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 | /// 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 | |
105 | impl 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" )] |
393 | impl 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 | |
399 | impl<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 | |
407 | impl<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 | |
420 | impl 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 | |
434 | impl<'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 | |
443 | fn 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 | |