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 | fn value_op<T>( |
321 | &mut self, |
322 | v: Value, |
323 | decorate: bool, |
324 | op: impl FnOnce(&mut Vec<Item>, Value) -> T, |
325 | ) -> T { |
326 | let mut value = v; |
327 | if !self.is_empty() && decorate { |
328 | value.decorate(" " , "" ); |
329 | } else if decorate { |
330 | value.decorate("" , "" ); |
331 | } |
332 | op(&mut self.values, value) |
333 | } |
334 | } |
335 | |
336 | impl std::fmt::Display for Array { |
337 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
338 | crate::encode::Encode::encode(self, buf:f, input:None, ("" , "" )) |
339 | } |
340 | } |
341 | |
342 | impl<V: Into<Value>> Extend<V> for Array { |
343 | fn extend<T: IntoIterator<Item = V>>(&mut self, iter: T) { |
344 | for value: V in iter { |
345 | self.push_formatted(value.into()); |
346 | } |
347 | } |
348 | } |
349 | |
350 | impl<V: Into<Value>> FromIterator<V> for Array { |
351 | fn from_iter<I>(iter: I) -> Self |
352 | where |
353 | I: IntoIterator<Item = V>, |
354 | { |
355 | let v: impl Iterator = iter.into_iter().map(|a: V| Item::Value(a.into())); |
356 | Array { |
357 | values: v.collect(), |
358 | ..Default::default() |
359 | } |
360 | } |
361 | } |
362 | |
363 | impl IntoIterator for Array { |
364 | type Item = Value; |
365 | type IntoIter = ArrayIntoIter; |
366 | |
367 | fn into_iter(self) -> Self::IntoIter { |
368 | Box::new( |
369 | self.values |
370 | .into_iter() |
371 | .filter(|v: &Item| v.is_value()) |
372 | .map(|v: Item| v.into_value().unwrap()), |
373 | ) |
374 | } |
375 | } |
376 | |
377 | impl<'s> IntoIterator for &'s Array { |
378 | type Item = &'s Value; |
379 | type IntoIter = ArrayIter<'s>; |
380 | |
381 | fn into_iter(self) -> Self::IntoIter { |
382 | self.iter() |
383 | } |
384 | } |
385 | |
386 | fn decorate_array(array: &mut Array) { |
387 | for (i: usize, value: &mut Value) in arrayimpl Iterator |
388 | .values |
389 | .iter_mut() |
390 | .filter_map(Item::as_value_mut) |
391 | .enumerate() |
392 | { |
393 | // [value1, value2, value3] |
394 | if i == 0 { |
395 | value.decorate(DEFAULT_LEADING_VALUE_DECOR.0, DEFAULT_LEADING_VALUE_DECOR.1); |
396 | } else { |
397 | value.decorate(DEFAULT_VALUE_DECOR.0, DEFAULT_VALUE_DECOR.1); |
398 | } |
399 | } |
400 | // Since everything is now on the same line, remove trailing commas and whitespace. |
401 | array.set_trailing_comma(yes:false); |
402 | array.set_trailing("" ); |
403 | } |
404 | |