1 /// The Toml Delete extensions
5 use crate::tokenizer
::Token
;
6 use crate::tokenizer
::tokenize_with_seperator
;
7 use crate::error
::{Error, Result}
;
9 pub trait TomlValueDeleteExt
{
11 /// Extension function for deleting a value in the current toml::Value document
12 /// using a custom seperator.
16 /// The function does _not_ delete non-empty data structures. So deleting `array` from
26 /// If the delete operation worked correctly, `Ok(Option<Value>)` is returned.
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))`.
35 /// On failure, `Err(e)` is returned
37 fn delete_with_seperator(&mut self, query
: &str, sep
: char) -> Result
<Option
<Value
>>;
39 /// Extension function for inserting a value from the current toml::Value document
41 /// See documentation of `TomlValueinsertExt::insert_with_seperator`
42 fn delete(&mut self, query
: &str) -> Result
<Option
<Value
>> {
43 self.delete_with_seperator(query
, '
.'
)
48 impl TomlValueDeleteExt
for Value
{
50 fn delete_with_seperator(&mut self, query
: &str, sep
: char) -> Result
<Option
<Value
>> {
51 use crate::resolver
::mut_resolver
::resolve
;
54 let mut tokens
= r
#try!(tokenize_with_seperator(query, sep));
55 let last_token
= tokens
.pop_last();
57 /// Check whether a structure (Table/Array) is empty. If the Value has not these types,
58 /// the default value is returned
60 fn is_empty(val
: Option
<&Value
>, default: bool
) -> bool
{
62 &Value
::Table(ref tab
) => tab
.is_empty(),
63 &Value
::Array(ref arr
) => arr
.is_empty(),
70 fn is_table(val
: Option
<&Value
>) -> bool
{
71 val
.map(|v
| is_match
!(v
, &Value
::Table(_
))).unwrap_or(false)
75 fn is_array(val
: Option
<&Value
>) -> bool
{
76 val
.map(|v
| is_match
!(v
, &Value
::Array(_
))).unwrap_or(false)
80 fn name_of_val(val
: Option
<&Value
>) -> &'
static str {
81 val
.map(crate::util
::name_of_val
).unwrap_or("None")
84 if last_token
.is_none() {
86 &mut Value
::Table(ref mut tab
) => {
88 Token
::Identifier { ident, .. }
=> {
89 if is_empty(tab
.get(&ident
), true) {
90 Ok(tab
.remove(&ident
))
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())))
97 let act
= name_of_val(tab
.get(&ident
));
99 Err(Error
::CannotAccessBecauseTypeMismatch(tbl
, act
))
106 &mut Value
::Array(ref mut arr
) => {
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
)))
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
))
118 let act
= name_of_val(Some(arr
.index(idx
)));
120 Err(Error
::CannotAccessBecauseTypeMismatch(tbl
, act
))
127 let kind
= match tokens
{
128 Token
::Identifier { ident, .. }
=> Error
::QueryingValueAsTable(ident
),
129 Token
::Index { idx , .. }
=> Error
::QueryingValueAsArray(idx
),
131 Err(Error
::from(kind
))
135 let val
= r
#try!(resolve(self, &tokens, true))
136 .unwrap(); // safe because of resolve() guarantees
137 let last_token
= last_token
.unwrap();
139 &mut Value
::Table(ref mut tab
) => {
141 Token
::Identifier { ref ident, .. }
=> {
142 if is_empty(tab
.get(ident
), true) {
143 Ok(tab
.remove(ident
))
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())))
150 let act
= name_of_val(tab
.get(ident
));
152 Err(Error
::CannotAccessBecauseTypeMismatch(tbl
, act
))
156 Token
::Index { idx, .. }
=> Err(Error
::NoIndexInTable(idx
)),
159 &mut Value
::Array(ref mut arr
) => {
161 Token
::Identifier { ident, .. }
=> Err(Error
::NoIdentifierInArray(ident
)),
162 Token
::Index { idx, .. }
=> {
164 return Err(Error
::ArrayIndexOutOfBounds(idx
, arr
.len()))
166 if is_empty(Some(&arr
.index(idx
)), true) {
167 Ok(Some(arr
.remove(idx
)))
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
))
174 let act
= name_of_val(Some(arr
.index(idx
)));
176 Err(Error
::CannotAccessBecauseTypeMismatch(tbl
, act
))
183 let kind
= match *last_token
{
184 Token
::Identifier { ident, .. }
=> Error
::QueryingValueAsTable(ident
),
185 Token
::Index { idx, .. }
=> Error
::QueryingValueAsArray(idx
),
187 Err(Error
::from(kind
))
199 use toml
::from_str
as toml_from_str
;
202 fn test_delete_from_empty_document() {
203 let mut toml
: Value
= toml_from_str("").unwrap();
205 let res
= toml
.delete_with_seperator(&String
::from("a"), '
.'
);
207 assert
!(res
.is_ok());
209 let res
= res
.unwrap();
210 assert
!(res
.is_none());
214 fn test_delete_from_empty_table() {
215 let mut toml
: Value
= toml_from_str(r
#"
219 let res
= toml
.delete_with_seperator(&String
::from("table.a"), '
.'
);
221 assert
!(res
.is_ok());
223 let res
= res
.unwrap();
224 assert
!(res
.is_none());
228 fn test_delete_integer() {
229 let mut toml
: Value
= toml_from_str(r
#"
233 let res
= toml
.delete_with_seperator(&String
::from("value"), '
.'
);
235 assert
!(res
.is_ok());
237 let res
= res
.unwrap();
238 assert
!(res
.is_some());
239 let res
= res
.unwrap();
240 assert
!(is_match
!(res
, Value
::Integer(1)));
244 fn test_delete_integer_removes_entry_from_document() {
245 let mut toml
: Value
= toml_from_str(r
#"
249 let res
= toml
.delete_with_seperator(&String
::from("value"), '
.'
);
251 assert
!(res
.is_ok());
253 let res
= res
.unwrap();
254 assert
!(res
.is_some());
255 let res
= res
.unwrap();
256 assert
!(is_match
!(res
, Value
::Integer(1)));
259 Value
::Table(tab
) => assert
!(tab
.is_empty()),
260 _
=> assert
!(false, "Strange things are happening"),
265 fn test_delete_string() {
266 let mut toml
: Value
= toml_from_str(r
#"
270 let res
= toml
.delete_with_seperator(&String
::from("value"), '
.'
);
272 assert
!(res
.is_ok());
274 let res
= res
.unwrap();
275 assert
!(res
.is_some());
276 let res
= res
.unwrap();
277 assert
!(is_match
!(res
, Value
::String(_
)));
279 Value
::String(ref s
) => assert_eq
!("foo", s
),
280 _
=> panic
!("What just happened?"),
285 fn test_delete_string_removes_entry_from_document() {
286 let mut toml
: Value
= toml_from_str(r
#"
290 let res
= toml
.delete_with_seperator(&String
::from("value"), '
.'
);
292 assert
!(res
.is_ok());
294 let res
= res
.unwrap();
295 assert
!(res
.is_some());
296 let res
= res
.unwrap();
297 assert
!(is_match
!(res
, Value
::String(_
)));
299 Value
::String(ref s
) => assert_eq
!("foo", s
),
300 _
=> panic
!("What just happened?"),
304 Value
::Table(tab
) => assert
!(tab
.is_empty()),
305 _
=> assert
!(false, "Strange things are happening"),
310 fn test_delete_empty_table() {
311 let mut toml
: Value
= toml_from_str(r
#"
315 let res
= toml
.delete_with_seperator(&String
::from("table"), '
.'
);
317 assert
!(res
.is_ok());
319 let res
= res
.unwrap();
320 assert
!(res
.is_some());
321 let res
= res
.unwrap();
322 assert
!(is_match
!(res
, Value
::Table(_
)));
324 Value
::Table(ref t
) => assert
!(t
.is_empty()),
325 _
=> panic
!("What just happened?"),
330 fn test_delete_empty_table_removes_entry_from_document() {
331 let mut toml
: Value
= toml_from_str(r
#"
335 let res
= toml
.delete_with_seperator(&String
::from("table"), '
.'
);
337 assert
!(res
.is_ok());
339 let res
= res
.unwrap();
340 assert
!(res
.is_some());
341 let res
= res
.unwrap();
342 assert
!(is_match
!(res
, Value
::Table(_
)));
344 Value
::Table(ref t
) => assert
!(t
.is_empty()),
345 _
=> panic
!("What just happened?"),
349 Value
::Table(tab
) => assert
!(tab
.is_empty()),
350 _
=> assert
!(false, "Strange things are happening"),
355 fn test_delete_empty_array() {
356 let mut toml
: Value
= toml_from_str(r
#"
360 let res
= toml
.delete_with_seperator(&String
::from("array"), '
.'
);
362 assert
!(res
.is_ok());
364 let res
= res
.unwrap();
365 assert
!(res
.is_some());
366 let res
= res
.unwrap();
367 assert
!(is_match
!(res
, Value
::Array(_
)));
369 Value
::Array(ref a
) => assert
!(a
.is_empty()),
370 _
=> panic
!("What just happened?"),
375 fn test_delete_empty_array_removes_entry_from_document() {
376 let mut toml
: Value
= toml_from_str(r
#"
380 let res
= toml
.delete_with_seperator(&String
::from("array"), '
.'
);
382 assert
!(res
.is_ok());
384 let res
= res
.unwrap();
385 assert
!(res
.is_some());
386 let res
= res
.unwrap();
387 assert
!(is_match
!(res
, Value
::Array(_
)));
389 Value
::Array(ref a
) => assert
!(a
.is_empty()),
390 _
=> panic
!("What just happened?"),
394 Value
::Table(tab
) => assert
!(tab
.is_empty()),
395 _
=> assert
!(false, "Strange things are happening"),
400 fn test_delete_nonempty_table() {
401 let mut toml
: Value
= toml_from_str(r
#"
406 let res
= toml
.delete_with_seperator(&String
::from("table"), '
.'
);
408 assert
!(res
.is_err());
410 let res
= res
.unwrap_err();
411 assert
!(is_match
!(res
, Error
::CannotDeleteNonEmptyTable(_
)));
415 fn test_delete_nonempty_array() {
416 let mut toml
: Value
= toml_from_str(r
#"
420 let res
= toml
.delete_with_seperator(&String
::from("array"), '
.'
);
422 assert
!(res
.is_err());
424 let res
= res
.unwrap_err();
425 assert
!(is_match
!(res
, Error
::CannotDeleteNonEmptyArray(_
)));
429 fn test_delete_int_from_table() {
430 let mut toml
: Value
= toml_from_str(r
#"
435 let res
= toml
.delete_with_seperator(&String
::from("table.int"), '
.'
);
437 assert
!(res
.is_ok());
439 let res
= res
.unwrap();
440 assert
!(is_match
!(res
, Some(Value
::Integer(1))));
444 fn test_delete_array_from_table() {
445 let mut toml
: Value
= toml_from_str(r
#"
450 let res
= toml
.delete_with_seperator(&String
::from("table.array"), '
.'
);
452 assert
!(res
.is_ok());
454 let res
= res
.unwrap();
455 assert
!(is_match
!(res
, Some(Value
::Array(_
))));
459 fn test_delete_int_from_array_from_table() {
460 let mut toml
: Value
= toml_from_str(r
#"
465 let res
= toml
.delete_with_seperator(&String
::from("table.array.[0]"), '
.'
);
467 assert
!(res
.is_ok());
469 let res
= res
.unwrap();
470 assert
!(is_match
!(res
, Some(Value
::Integer(1))));
474 fn test_delete_int_from_array() {
475 let mut toml
: Value
= toml_from_str(r
#"
479 let res
= toml
.delete_with_seperator(&String
::from("array.[0]"), '
.'
);
481 assert
!(res
.is_ok());
483 let res
= res
.unwrap();
484 assert
!(is_match
!(res
, Some(Value
::Integer(1))));
488 fn test_delete_int_from_table_from_array() {
489 let mut toml
: Value
= toml_from_str(r
#"
490 array = [ { table = { int = 1 } } ]
493 let res
= toml
.delete_with_seperator(&String
::from("array.[0].table.int"), '
.'
);
495 assert
!(res
.is_ok());
497 let res
= res
.unwrap();
498 assert
!(is_match
!(res
, Some(Value
::Integer(1))));
502 fn test_delete_from_array_value() {
503 use crate::read
::TomlValueReadExt
;
505 let mut toml
: Value
= toml_from_str(r
#"
509 let ary
= toml
.read_mut(&String
::from("array")).unwrap().unwrap();
510 let res
= ary
.delete_with_seperator(&String
::from("[0]"), '
.'
);
512 assert
!(res
.is_ok());
514 let res
= res
.unwrap();
515 assert
!(is_match
!(res
, Some(Value
::Integer(1))));
519 fn test_delete_from_int_value() {
520 use crate::read
::TomlValueReadExt
;
522 let mut toml
: Value
= toml_from_str(r
#"
526 let ary
= toml
.read_mut(&String
::from("array.[0]")).unwrap().unwrap();
527 let res
= ary
.delete_with_seperator(&String
::from("nonexist"), '
.'
);
529 assert
!(res
.is_err());
531 let res
= res
.unwrap_err();
532 assert
!(is_match
!(res
, Error
::QueryingValueAsTable(_
)));
536 fn test_delete_index_from_non_array() {
537 use crate::read
::TomlValueReadExt
;
539 let mut toml
: Value
= toml_from_str(r
#"
543 let ary
= toml
.read_mut(&String
::from("array")).unwrap().unwrap();
544 let res
= ary
.delete_with_seperator(&String
::from("[0]"), '
.'
);
546 assert
!(res
.is_err());
548 let res
= res
.unwrap_err();
549 assert
!(is_match
!(res
, Error
::QueryingValueAsArray(_
)));
553 fn test_delete_index_from_table_in_table() {
554 let mut toml
: Value
= toml_from_str(r
#"
555 table = { another = { int = 1 } }
558 let res
= toml
.delete_with_seperator(&String
::from("table.another.[0]"), '
.'
);
560 assert
!(res
.is_err());
562 let res
= res
.unwrap_err();
563 assert
!(is_match
!(res
, Error
::NoIndexInTable(0)));
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 ] }
572 let res
= toml
.delete_with_seperator(&String
::from("table.another.nonexist"), '
.'
);
574 assert
!(res
.is_err());
576 let res
= res
.unwrap_err();
577 assert
!(is_match
!(res
, Error
::NoIdentifierInArray(_
)));
581 fn test_delete_nonexistent_array_idx() {
582 let mut toml
: Value
= toml_from_str(r
#"
586 let res
= toml
.delete_with_seperator(&String
::from("array.[22]"), '
.'
);
588 assert
!(res
.is_err());
590 let res
= res
.unwrap_err();
591 assert
!(is_match
!(res
, Error
::ArrayIndexOutOfBounds(22, 3)));
595 fn test_delete_non_empty_array_from_array() {
596 let mut toml
: Value
= toml_from_str(r
#"
597 array = [ [ 1 ], [ 2 ] ]
600 let res
= toml
.delete_with_seperator(&String
::from("array.[1]"), '
.'
);
602 assert
!(res
.is_err());
604 let res
= res
.unwrap_err();
605 assert
!(is_match
!(res
, Error
::CannotDeleteNonEmptyArray(None
)));
609 fn test_delete_non_empty_table_from_array() {
610 let mut toml
: Value
= toml_from_str(r
#"
611 array = [ { t = 1 }, { t = 2 } ]
614 let res
= toml
.delete_with_seperator(&String
::from("array.[1]"), '
.'
);
616 assert
!(res
.is_err());
618 let res
= res
.unwrap_err();
619 assert
!(is_match
!(res
, Error
::CannotDeleteNonEmptyTable(None
)));
623 fn test_delete_non_empty_table_from_top_level_array() {
624 use crate::read
::TomlValueReadExt
;
626 let mut toml
: Value
= toml_from_str(r
#"
627 array = [ { t = 1 }, { t = 2 } ]
630 let ary
= toml
.read_mut(&String
::from("array")).unwrap().unwrap();
631 let res
= ary
.delete_with_seperator(&String
::from("[1]"), '
.'
);
633 assert
!(res
.is_err());
635 let res
= res
.unwrap_err();
636 assert
!(is_match
!(res
, Error
::CannotDeleteNonEmptyTable(None
)));
640 fn test_delete_from_value_like_it_was_table() {
641 let mut toml
: Value
= toml_from_str(r
#"
645 let res
= toml
.delete_with_seperator(&String
::from("val.foo"), '
.'
);
647 assert
!(res
.is_err());
649 let res
= res
.unwrap_err();
650 assert
!(is_match
!(res
, Error
::QueryingValueAsTable(_
)));
654 fn test_delete_from_value_like_it_was_array() {
655 let mut toml
: Value
= toml_from_str(r
#"
659 let res
= toml
.delete_with_seperator(&String
::from("val.[0]"), '
.'
);
661 assert
!(res
.is_err());
663 let res
= res
.unwrap_err();
664 assert
!(is_match
!(res
, Error
::QueryingValueAsArray(0)));