|
2 | 2 |
|
3 | 3 | use crate::{ |
4 | 4 | de::key::QNameDeserializer, |
5 | | - de::seq::{not_in, TagFilter}, |
6 | 5 | de::simple_type::SimpleTypeDeserializer, |
7 | 6 | de::{str2bool, DeEvent, Deserializer, XmlRead, TEXT_KEY, VALUE_KEY}, |
| 7 | + encoding::Decoder, |
8 | 8 | errors::serialize::DeError, |
9 | 9 | events::attributes::IterState, |
10 | 10 | events::BytesStart, |
@@ -144,6 +144,8 @@ enum ValueSource { |
144 | 144 | Nested, |
145 | 145 | } |
146 | 146 |
|
| 147 | +//////////////////////////////////////////////////////////////////////////////////////////////////// |
| 148 | + |
147 | 149 | /// A deserializer that extracts map-like structures from an XML. This deserializer |
148 | 150 | /// represents a one XML tag: |
149 | 151 | /// |
@@ -549,6 +551,72 @@ where |
549 | 551 |
|
550 | 552 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
551 | 553 |
|
| 554 | +/// Check if tag `start` is included in the `fields` list. `decoder` is used to |
| 555 | +/// get a string representation of a tag. |
| 556 | +/// |
| 557 | +/// Returns `true`, if `start` is not in the `fields` list and `false` otherwise. |
| 558 | +fn not_in( |
| 559 | + fields: &'static [&'static str], |
| 560 | + start: &BytesStart, |
| 561 | + decoder: Decoder, |
| 562 | +) -> Result<bool, DeError> { |
| 563 | + let tag = decoder.decode(start.name().into_inner())?; |
| 564 | + |
| 565 | + Ok(fields.iter().all(|&field| field != tag.as_ref())) |
| 566 | +} |
| 567 | + |
| 568 | +/// A filter that determines, what tags should form a sequence. |
| 569 | +/// |
| 570 | +/// There are two types of sequences: |
| 571 | +/// - sequence where each element represented by tags with the same name |
| 572 | +/// - sequence where each element can have a different tag |
| 573 | +/// |
| 574 | +/// The first variant could represent a collection of structs, the second -- |
| 575 | +/// a collection of enum variants. |
| 576 | +/// |
| 577 | +/// In the second case we don't know what tag name should be expected as a |
| 578 | +/// sequence element, so we accept any element. Since the sequence are flattened |
| 579 | +/// into maps, we skip elements which have dedicated fields in a struct by using an |
| 580 | +/// `Exclude` filter that filters out elements with names matching field names |
| 581 | +/// from the struct. |
| 582 | +/// |
| 583 | +/// # Lifetimes |
| 584 | +/// |
| 585 | +/// `'de` represents a lifetime of the XML input, when filter stores the |
| 586 | +/// dedicated tag name |
| 587 | +#[derive(Debug)] |
| 588 | +enum TagFilter<'de> { |
| 589 | + /// A `SeqAccess` interested only in tags with specified name to deserialize |
| 590 | + /// an XML like this: |
| 591 | + /// |
| 592 | + /// ```xml |
| 593 | + /// <...> |
| 594 | + /// <tag/> |
| 595 | + /// <tag/> |
| 596 | + /// <tag/> |
| 597 | + /// ... |
| 598 | + /// </...> |
| 599 | + /// ``` |
| 600 | + /// |
| 601 | + /// The tag name is stored inside (`b"tag"` for that example) |
| 602 | + Include(BytesStart<'de>), //TODO: Need to store only name instead of a whole tag |
| 603 | + /// A `SeqAccess` interested in tags with any name, except explicitly listed. |
| 604 | + /// Excluded tags are used as struct field names and therefore should not |
| 605 | + /// fall into a `$value` category |
| 606 | + Exclude(&'static [&'static str]), |
| 607 | +} |
| 608 | + |
| 609 | +impl<'de> TagFilter<'de> { |
| 610 | + fn is_suitable(&self, start: &BytesStart, decoder: Decoder) -> Result<bool, DeError> { |
| 611 | + match self { |
| 612 | + Self::Include(n) => Ok(n.name() == start.name()), |
| 613 | + Self::Exclude(fields) => not_in(fields, start, decoder), |
| 614 | + } |
| 615 | + } |
| 616 | +} |
| 617 | + |
| 618 | +//////////////////////////////////////////////////////////////////////////////////////////////////// |
| 619 | + |
552 | 620 | /// An accessor to sequence elements forming a value for struct field. |
553 | 621 | /// Technically, this sequence is flattened out into structure and sequence |
554 | 622 | /// elements are overlapped with other fields of a structure |
@@ -750,3 +818,20 @@ where |
750 | 818 | self.map.de.is_human_readable() |
751 | 819 | } |
752 | 820 | } |
| 821 | + |
| 822 | +//////////////////////////////////////////////////////////////////////////////////////////////////// |
| 823 | + |
| 824 | +#[test] |
| 825 | +fn test_not_in() { |
| 826 | + let tag = BytesStart::new("tag"); |
| 827 | + |
| 828 | + assert_eq!(not_in(&[], &tag, Decoder::utf8()).unwrap(), true); |
| 829 | + assert_eq!( |
| 830 | + not_in(&["no", "such", "tags"], &tag, Decoder::utf8()).unwrap(), |
| 831 | + true |
| 832 | + ); |
| 833 | + assert_eq!( |
| 834 | + not_in(&["some", "tag", "included"], &tag, Decoder::utf8()).unwrap(), |
| 835 | + false |
| 836 | + ); |
| 837 | +} |
0 commit comments