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