]> git.proxmox.com Git - rustc.git/blob - vendor/toml-query/src/delete.rs
New upstream version 1.45.0+dfsg1
[rustc.git] / vendor / toml-query / src / delete.rs
1 /// The Toml Delete extensions
2
3 use toml::Value;
4
5 use crate::tokenizer::Token;
6 use crate::tokenizer::tokenize_with_seperator;
7 use crate::error::{Error, Result};
8
9 pub trait TomlValueDeleteExt {
10
11 /// Extension function for deleting a value in the current toml::Value document
12 /// using a custom seperator.
13 ///
14 /// # Semantics
15 ///
16 /// The function does _not_ delete non-empty data structures. So deleting `array` from
17 ///
18 /// ```toml
19 /// array = [ 1 ]
20 /// ```
21 ///
22 /// does _not_ work.
23 ///
24 /// # Return value
25 ///
26 /// If the delete operation worked correctly, `Ok(Option<Value>)` is returned.
27 ///
28 /// The `Option<Value>` part is `None` if no value was actually removed as there was no value
29 /// there. For example, if you're deleting `table.a` and the Table `table` has no key `a`, then
30 /// `Ok(None)` is returned. Also, if you're deleting from an Array, but there is nothing in the
31 /// array, or the array is shorter than the index you're deleting.
32 /// If the delete operation actually removed something from the toml document, this value is
33 /// returned as `Ok(Some(Value))`.
34 ///
35 /// On failure, `Err(e)` is returned
36 ///
37 fn delete_with_seperator(&mut self, query: &str, sep: char) -> Result<Option<Value>>;
38
39 /// Extension function for inserting a value from the current toml::Value document
40 ///
41 /// See documentation of `TomlValueinsertExt::insert_with_seperator`
42 fn delete(&mut self, query: &str) -> Result<Option<Value>> {
43 self.delete_with_seperator(query, '.')
44 }
45
46 }
47
48 impl TomlValueDeleteExt for Value {
49
50 fn delete_with_seperator(&mut self, query: &str, sep: char) -> Result<Option<Value>> {
51 use crate::resolver::mut_resolver::resolve;
52 use std::ops::Index;
53
54 let mut tokens = r#try!(tokenize_with_seperator(query, sep));
55 let last_token = tokens.pop_last();
56
57 /// Check whether a structure (Table/Array) is empty. If the Value has not these types,
58 /// the default value is returned
59 #[inline]
60 fn is_empty(val: Option<&Value>, default: bool) -> bool {
61 val.map(|v| match v {
62 &Value::Table(ref tab) => tab.is_empty(),
63 &Value::Array(ref arr) => arr.is_empty(),
64 _ => default
65 })
66 .unwrap_or(default)
67 }
68
69 #[inline]
70 fn is_table(val: Option<&Value>) -> bool {
71 val.map(|v| is_match!(v, &Value::Table(_))).unwrap_or(false)
72 }
73
74 #[inline]
75 fn is_array(val: Option<&Value>) -> bool {
76 val.map(|v| is_match!(v, &Value::Array(_))).unwrap_or(false)
77 }
78
79 #[inline]
80 fn name_of_val(val: Option<&Value>) -> &'static str {
81 val.map(crate::util::name_of_val).unwrap_or("None")
82 }
83
84 if last_token.is_none() {
85 match self {
86 &mut Value::Table(ref mut tab) => {
87 match tokens {
88 Token::Identifier { ident, .. } => {
89 if is_empty(tab.get(&ident), true) {
90 Ok(tab.remove(&ident))
91 } else {
92 if is_table(tab.get(&ident)) {
93 Err(Error::CannotDeleteNonEmptyTable(Some(ident.clone())))
94 } else if is_array(tab.get(&ident)) {
95 Err(Error::CannotDeleteNonEmptyArray(Some(ident.clone())))
96 } else {
97 let act = name_of_val(tab.get(&ident));
98 let tbl = "table";
99 Err(Error::CannotAccessBecauseTypeMismatch(tbl, act))
100 }
101 }
102 },
103 _ => Ok(None)
104 }
105 },
106 &mut Value::Array(ref mut arr) => {
107 match tokens {
108 Token::Identifier { ident, .. } => Err(Error::NoIdentifierInArray(ident)),
109 Token::Index { idx , .. } => {
110 if is_empty(Some(arr.index(idx)), true) {
111 Ok(Some(arr.remove(idx)))
112 } else {
113 if is_table(Some(arr.index(idx))) {
114 Err(Error::CannotDeleteNonEmptyTable(None))
115 } else if is_array(Some(arr.index(idx))) {
116 Err(Error::CannotDeleteNonEmptyArray(None))
117 } else {
118 let act = name_of_val(Some(arr.index(idx)));
119 let tbl = "table";
120 Err(Error::CannotAccessBecauseTypeMismatch(tbl, act))
121 }
122 }
123 },
124 }
125 },
126 _ => {
127 let kind = match tokens {
128 Token::Identifier { ident, .. } => Error::QueryingValueAsTable(ident),
129 Token::Index { idx , .. } => Error::QueryingValueAsArray(idx),
130 };
131 Err(Error::from(kind))
132 }
133 }
134 } else {
135 let val = r#try!(resolve(self, &tokens, true))
136 .unwrap(); // safe because of resolve() guarantees
137 let last_token = last_token.unwrap();
138 match val {
139 &mut Value::Table(ref mut tab) => {
140 match *last_token {
141 Token::Identifier { ref ident, .. } => {
142 if is_empty(tab.get(ident), true) {
143 Ok(tab.remove(ident))
144 } else {
145 if is_table(tab.get(ident)) {
146 Err(Error::CannotDeleteNonEmptyTable(Some(ident.clone())))
147 } else if is_array(tab.get(ident)) {
148 Err(Error::CannotDeleteNonEmptyArray(Some(ident.clone())))
149 } else {
150 let act = name_of_val(tab.get(ident));
151 let tbl = "table";
152 Err(Error::CannotAccessBecauseTypeMismatch(tbl, act))
153 }
154 }
155 },
156 Token::Index { idx, .. } => Err(Error::NoIndexInTable(idx)),
157 }
158 },
159 &mut Value::Array(ref mut arr) => {
160 match *last_token {
161 Token::Identifier { ident, .. } => Err(Error::NoIdentifierInArray(ident)),
162 Token::Index { idx, .. } => {
163 if idx > arr.len() {
164 return Err(Error::ArrayIndexOutOfBounds(idx, arr.len()))
165 }
166 if is_empty(Some(&arr.index(idx)), true) {
167 Ok(Some(arr.remove(idx)))
168 } else {
169 if is_table(Some(&arr.index(idx))) {
170 Err(Error::CannotDeleteNonEmptyTable(None))
171 } else if is_array(Some(&arr.index(idx))) {
172 Err(Error::CannotDeleteNonEmptyArray(None))
173 } else {
174 let act = name_of_val(Some(arr.index(idx)));
175 let tbl = "table";
176 Err(Error::CannotAccessBecauseTypeMismatch(tbl, act))
177 }
178 }
179 },
180 }
181 },
182 _ => {
183 let kind = match *last_token {
184 Token::Identifier { ident, .. } => Error::QueryingValueAsTable(ident),
185 Token::Index { idx, .. } => Error::QueryingValueAsArray(idx),
186 };
187 Err(Error::from(kind))
188 }
189 }
190 }
191 }
192
193 }
194
195 #[cfg(test)]
196 mod test {
197 use super::*;
198 use toml::Value;
199 use toml::from_str as toml_from_str;
200
201 #[test]
202 fn test_delete_from_empty_document() {
203 let mut toml : Value = toml_from_str("").unwrap();
204
205 let res = toml.delete_with_seperator(&String::from("a"), '.');
206
207 assert!(res.is_ok());
208
209 let res = res.unwrap();
210 assert!(res.is_none());
211 }
212
213 #[test]
214 fn test_delete_from_empty_table() {
215 let mut toml : Value = toml_from_str(r#"
216 [table]
217 "#).unwrap();
218
219 let res = toml.delete_with_seperator(&String::from("table.a"), '.');
220
221 assert!(res.is_ok());
222
223 let res = res.unwrap();
224 assert!(res.is_none());
225 }
226
227 #[test]
228 fn test_delete_integer() {
229 let mut toml : Value = toml_from_str(r#"
230 value = 1
231 "#).unwrap();
232
233 let res = toml.delete_with_seperator(&String::from("value"), '.');
234
235 assert!(res.is_ok());
236
237 let res = res.unwrap();
238 assert!(res.is_some());
239 let res = res.unwrap();
240 assert!(is_match!(res, Value::Integer(1)));
241 }
242
243 #[test]
244 fn test_delete_integer_removes_entry_from_document() {
245 let mut toml : Value = toml_from_str(r#"
246 value = 1
247 "#).unwrap();
248
249 let res = toml.delete_with_seperator(&String::from("value"), '.');
250
251 assert!(res.is_ok());
252
253 let res = res.unwrap();
254 assert!(res.is_some());
255 let res = res.unwrap();
256 assert!(is_match!(res, Value::Integer(1)));
257
258 match toml {
259 Value::Table(tab) => assert!(tab.is_empty()),
260 _ => assert!(false, "Strange things are happening"),
261 }
262 }
263
264 #[test]
265 fn test_delete_string() {
266 let mut toml : Value = toml_from_str(r#"
267 value = "foo"
268 "#).unwrap();
269
270 let res = toml.delete_with_seperator(&String::from("value"), '.');
271
272 assert!(res.is_ok());
273
274 let res = res.unwrap();
275 assert!(res.is_some());
276 let res = res.unwrap();
277 assert!(is_match!(res, Value::String(_)));
278 match res {
279 Value::String(ref s) => assert_eq!("foo", s),
280 _ => panic!("What just happened?"),
281 }
282 }
283
284 #[test]
285 fn test_delete_string_removes_entry_from_document() {
286 let mut toml : Value = toml_from_str(r#"
287 value = "foo"
288 "#).unwrap();
289
290 let res = toml.delete_with_seperator(&String::from("value"), '.');
291
292 assert!(res.is_ok());
293
294 let res = res.unwrap();
295 assert!(res.is_some());
296 let res = res.unwrap();
297 assert!(is_match!(res, Value::String(_)));
298 match res {
299 Value::String(ref s) => assert_eq!("foo", s),
300 _ => panic!("What just happened?"),
301 }
302
303 match toml {
304 Value::Table(tab) => assert!(tab.is_empty()),
305 _ => assert!(false, "Strange things are happening"),
306 }
307 }
308
309 #[test]
310 fn test_delete_empty_table() {
311 let mut toml : Value = toml_from_str(r#"
312 [table]
313 "#).unwrap();
314
315 let res = toml.delete_with_seperator(&String::from("table"), '.');
316
317 assert!(res.is_ok());
318
319 let res = res.unwrap();
320 assert!(res.is_some());
321 let res = res.unwrap();
322 assert!(is_match!(res, Value::Table(_)));
323 match res {
324 Value::Table(ref t) => assert!(t.is_empty()),
325 _ => panic!("What just happened?"),
326 }
327 }
328
329 #[test]
330 fn test_delete_empty_table_removes_entry_from_document() {
331 let mut toml : Value = toml_from_str(r#"
332 [table]
333 "#).unwrap();
334
335 let res = toml.delete_with_seperator(&String::from("table"), '.');
336
337 assert!(res.is_ok());
338
339 let res = res.unwrap();
340 assert!(res.is_some());
341 let res = res.unwrap();
342 assert!(is_match!(res, Value::Table(_)));
343 match res {
344 Value::Table(ref t) => assert!(t.is_empty()),
345 _ => panic!("What just happened?"),
346 }
347
348 match toml {
349 Value::Table(tab) => assert!(tab.is_empty()),
350 _ => assert!(false, "Strange things are happening"),
351 }
352 }
353
354 #[test]
355 fn test_delete_empty_array() {
356 let mut toml : Value = toml_from_str(r#"
357 array = []
358 "#).unwrap();
359
360 let res = toml.delete_with_seperator(&String::from("array"), '.');
361
362 assert!(res.is_ok());
363
364 let res = res.unwrap();
365 assert!(res.is_some());
366 let res = res.unwrap();
367 assert!(is_match!(res, Value::Array(_)));
368 match res {
369 Value::Array(ref a) => assert!(a.is_empty()),
370 _ => panic!("What just happened?"),
371 }
372 }
373
374 #[test]
375 fn test_delete_empty_array_removes_entry_from_document() {
376 let mut toml : Value = toml_from_str(r#"
377 array = []
378 "#).unwrap();
379
380 let res = toml.delete_with_seperator(&String::from("array"), '.');
381
382 assert!(res.is_ok());
383
384 let res = res.unwrap();
385 assert!(res.is_some());
386 let res = res.unwrap();
387 assert!(is_match!(res, Value::Array(_)));
388 match res {
389 Value::Array(ref a) => assert!(a.is_empty()),
390 _ => panic!("What just happened?"),
391 }
392
393 match toml {
394 Value::Table(tab) => assert!(tab.is_empty()),
395 _ => assert!(false, "Strange things are happening"),
396 }
397 }
398
399 #[test]
400 fn test_delete_nonempty_table() {
401 let mut toml : Value = toml_from_str(r#"
402 [table]
403 a = 1
404 "#).unwrap();
405
406 let res = toml.delete_with_seperator(&String::from("table"), '.');
407
408 assert!(res.is_err());
409
410 let res = res.unwrap_err();
411 assert!(is_match!(res, Error::CannotDeleteNonEmptyTable(_)));
412 }
413
414 #[test]
415 fn test_delete_nonempty_array() {
416 let mut toml : Value = toml_from_str(r#"
417 array = [ 1 ]
418 "#).unwrap();
419
420 let res = toml.delete_with_seperator(&String::from("array"), '.');
421
422 assert!(res.is_err());
423
424 let res = res.unwrap_err();
425 assert!(is_match!(res, Error::CannotDeleteNonEmptyArray(_)));
426 }
427
428 #[test]
429 fn test_delete_int_from_table() {
430 let mut toml : Value = toml_from_str(r#"
431 [table]
432 int = 1
433 "#).unwrap();
434
435 let res = toml.delete_with_seperator(&String::from("table.int"), '.');
436
437 assert!(res.is_ok());
438
439 let res = res.unwrap();
440 assert!(is_match!(res, Some(Value::Integer(1))));
441 }
442
443 #[test]
444 fn test_delete_array_from_table() {
445 let mut toml : Value = toml_from_str(r#"
446 [table]
447 array = []
448 "#).unwrap();
449
450 let res = toml.delete_with_seperator(&String::from("table.array"), '.');
451
452 assert!(res.is_ok());
453
454 let res = res.unwrap();
455 assert!(is_match!(res, Some(Value::Array(_))));
456 }
457
458 #[test]
459 fn test_delete_int_from_array_from_table() {
460 let mut toml : Value = toml_from_str(r#"
461 [table]
462 array = [ 1 ]
463 "#).unwrap();
464
465 let res = toml.delete_with_seperator(&String::from("table.array.[0]"), '.');
466
467 assert!(res.is_ok());
468
469 let res = res.unwrap();
470 assert!(is_match!(res, Some(Value::Integer(1))));
471 }
472
473 #[test]
474 fn test_delete_int_from_array() {
475 let mut toml : Value = toml_from_str(r#"
476 array = [ 1 ]
477 "#).unwrap();
478
479 let res = toml.delete_with_seperator(&String::from("array.[0]"), '.');
480
481 assert!(res.is_ok());
482
483 let res = res.unwrap();
484 assert!(is_match!(res, Some(Value::Integer(1))));
485 }
486
487 #[test]
488 fn test_delete_int_from_table_from_array() {
489 let mut toml : Value = toml_from_str(r#"
490 array = [ { table = { int = 1 } } ]
491 "#).unwrap();
492
493 let res = toml.delete_with_seperator(&String::from("array.[0].table.int"), '.');
494
495 assert!(res.is_ok());
496
497 let res = res.unwrap();
498 assert!(is_match!(res, Some(Value::Integer(1))));
499 }
500
501 #[test]
502 fn test_delete_from_array_value() {
503 use crate::read::TomlValueReadExt;
504
505 let mut toml : Value = toml_from_str(r#"
506 array = [ 1 ]
507 "#).unwrap();
508
509 let ary = toml.read_mut(&String::from("array")).unwrap().unwrap();
510 let res = ary.delete_with_seperator(&String::from("[0]"), '.');
511
512 assert!(res.is_ok());
513
514 let res = res.unwrap();
515 assert!(is_match!(res, Some(Value::Integer(1))));
516 }
517
518 #[test]
519 fn test_delete_from_int_value() {
520 use crate::read::TomlValueReadExt;
521
522 let mut toml : Value = toml_from_str(r#"
523 array = [ 1 ]
524 "#).unwrap();
525
526 let ary = toml.read_mut(&String::from("array.[0]")).unwrap().unwrap();
527 let res = ary.delete_with_seperator(&String::from("nonexist"), '.');
528
529 assert!(res.is_err());
530
531 let res = res.unwrap_err();
532 assert!(is_match!(res, Error::QueryingValueAsTable(_)));
533 }
534
535 #[test]
536 fn test_delete_index_from_non_array() {
537 use crate::read::TomlValueReadExt;
538
539 let mut toml : Value = toml_from_str(r#"
540 array = 1
541 "#).unwrap();
542
543 let ary = toml.read_mut(&String::from("array")).unwrap().unwrap();
544 let res = ary.delete_with_seperator(&String::from("[0]"), '.');
545
546 assert!(res.is_err());
547
548 let res = res.unwrap_err();
549 assert!(is_match!(res, Error::QueryingValueAsArray(_)));
550 }
551
552 #[test]
553 fn test_delete_index_from_table_in_table() {
554 let mut toml : Value = toml_from_str(r#"
555 table = { another = { int = 1 } }
556 "#).unwrap();
557
558 let res = toml.delete_with_seperator(&String::from("table.another.[0]"), '.');
559
560 assert!(res.is_err());
561
562 let res = res.unwrap_err();
563 assert!(is_match!(res, Error::NoIndexInTable(0)));
564 }
565
566 #[test]
567 fn test_delete_identifier_from_array_in_table() {
568 let mut toml : Value = toml_from_str(r#"
569 table = { another = [ 1, 2, 3, 4, 5, 6 ] }
570 "#).unwrap();
571
572 let res = toml.delete_with_seperator(&String::from("table.another.nonexist"), '.');
573
574 assert!(res.is_err());
575
576 let res = res.unwrap_err();
577 assert!(is_match!(res, Error::NoIdentifierInArray(_)));
578 }
579
580 #[test]
581 fn test_delete_nonexistent_array_idx() {
582 let mut toml : Value = toml_from_str(r#"
583 array = [ 1, 2, 3 ]
584 "#).unwrap();
585
586 let res = toml.delete_with_seperator(&String::from("array.[22]"), '.');
587
588 assert!(res.is_err());
589
590 let res = res.unwrap_err();
591 assert!(is_match!(res, Error::ArrayIndexOutOfBounds(22, 3)));
592 }
593
594 #[test]
595 fn test_delete_non_empty_array_from_array() {
596 let mut toml : Value = toml_from_str(r#"
597 array = [ [ 1 ], [ 2 ] ]
598 "#).unwrap();
599
600 let res = toml.delete_with_seperator(&String::from("array.[1]"), '.');
601
602 assert!(res.is_err());
603
604 let res = res.unwrap_err();
605 assert!(is_match!(res, Error::CannotDeleteNonEmptyArray(None)));
606 }
607
608 #[test]
609 fn test_delete_non_empty_table_from_array() {
610 let mut toml : Value = toml_from_str(r#"
611 array = [ { t = 1 }, { t = 2 } ]
612 "#).unwrap();
613
614 let res = toml.delete_with_seperator(&String::from("array.[1]"), '.');
615
616 assert!(res.is_err());
617
618 let res = res.unwrap_err();
619 assert!(is_match!(res, Error::CannotDeleteNonEmptyTable(None)));
620 }
621
622 #[test]
623 fn test_delete_non_empty_table_from_top_level_array() {
624 use crate::read::TomlValueReadExt;
625
626 let mut toml : Value = toml_from_str(r#"
627 array = [ { t = 1 }, { t = 2 } ]
628 "#).unwrap();
629
630 let ary = toml.read_mut(&String::from("array")).unwrap().unwrap();
631 let res = ary.delete_with_seperator(&String::from("[1]"), '.');
632
633 assert!(res.is_err());
634
635 let res = res.unwrap_err();
636 assert!(is_match!(res, Error::CannotDeleteNonEmptyTable(None)));
637 }
638
639 #[test]
640 fn test_delete_from_value_like_it_was_table() {
641 let mut toml : Value = toml_from_str(r#"
642 val = 5
643 "#).unwrap();
644
645 let res = toml.delete_with_seperator(&String::from("val.foo"), '.');
646
647 assert!(res.is_err());
648
649 let res = res.unwrap_err();
650 assert!(is_match!(res, Error::QueryingValueAsTable(_)));
651 }
652
653 #[test]
654 fn test_delete_from_value_like_it_was_array() {
655 let mut toml : Value = toml_from_str(r#"
656 val = 5
657 "#).unwrap();
658
659 let res = toml.delete_with_seperator(&String::from("val.[0]"), '.');
660
661 assert!(res.is_err());
662
663 let res = res.unwrap_err();
664 assert!(is_match!(res, Error::QueryingValueAsArray(0)));
665 }
666
667 }
668