1use std::fmt;
5use std::io::Read;
6use std::marker::PhantomData;
7
8#[doc(hidden)]
9pub use paste::item as paste_item;
10use serde::de::DeserializeOwned;
11use tuple_utils::Prepend;
12
13use crate::de::{DeserializeError, Deserializer};
14use crate::parser::{EventCode, Parser};
15
16pub trait DeTuple {
21 type Output;
23
24 fn detuple(self) -> Self::Output;
26}
27
28impl<A> DeTuple for (A,) {
29 type Output = A;
30 fn detuple(self) -> Self::Output { self.0 }
31}
32
33macro_rules! detuple_impls {
34 ($i:ident, $j:ident) => {
35 detuple_impls!(impl $i, $j);
36 };
37
38 ($i:ident, $j:ident, $($r_i:ident),+) => {
39 detuple_impls!(impl $i, $j, $($r_i),+);
40 detuple_impls!($j, $($r_i),+);
41 };
42
43 (impl $($i:ident),+) => {
44 impl<$($i),+> DeTuple for ($($i),+) {
45 type Output = ($($i),+);
46 fn detuple(self) -> Self::Output { self }
47 }
48 };
49}
50
51detuple_impls!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
52
53
54macro_rules! try_some {
56 ($e:expr) => {
57 match $e {
58 Ok(v) => v,
59 Err(err) => return Some(Err(core::convert::From::from(err)))
60 }
61 }
62}
63
64pub trait TagMatcher {
66 fn matches(&self, tag_name: &str) -> bool;
68}
69
70#[derive(Debug, PartialEq)]
72pub struct ExactTagMatch {
73 needle: &'static str,
74}
75
76impl TagMatcher for ExactTagMatch {
77 #[inline(always)]
78 fn matches(&self, tag_name: &str) -> bool {
79 self.needle == tag_name
80 }
81}
82
83#[derive(Debug, Default, PartialEq)]
85pub struct AnyTagMatch {}
86
87impl TagMatcher for AnyTagMatch {
88 #[inline(always)]
89 fn matches(&self, _tag_name: &str) -> bool {
90 true
91 }
92}
93
94#[derive(Debug)]
100pub struct ElementEnter<M, N> {
101 tag_matcher: M,
102
103 pub next: N,
105
106 entered: bool,
107}
108
109impl<M: Default, N: Default> Default for ElementEnter<M, N> {
110 fn default() -> Self {
111 Self {
112 tag_matcher: M::default(),
113 next: N::default(),
114 entered: false,
115 }
116 }
117}
118
119impl<M, N> ElementEnter<M, N> {
120 pub fn new(tag_matcher: M, next: N) -> Self {
122 Self { tag_matcher, next, entered: false }
123 }
124}
125
126impl<N> ElementEnter<ExactTagMatch, N> {
127 pub fn tag(tag: &'static str, next: N) -> Self {
129 Self::new(ExactTagMatch { needle: tag }, next)
130 }
131}
132
133impl<N> ElementEnter<AnyTagMatch, N> {
134 pub fn any(next: N) -> Self {
136 Self::new(AnyTagMatch {}, next)
137 }
138}
139
140pub struct ElementEnterDeserialize<T, M, N> {
148 tag_matcher: M,
149
150 pub next: N,
152
153 trace: Option<Box<dyn FnMut(&T)>>,
154
155 entered: Option<T>,
156}
157
158impl<T, M, N> fmt::Debug for ElementEnterDeserialize<T, M, N>
159 where T: fmt::Debug,
160 M: fmt::Debug,
161 N: fmt::Debug,
162{
163 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164 f.debug_struct("ElementEnterDeserialize")
165 .field("tag_matcher", &self.tag_matcher)
166 .field("next", &self.next)
167 .field("entered", &self.entered)
168 .finish_non_exhaustive()
169 }
170}
171
172impl<T, M: Default, N: Default> Default for ElementEnterDeserialize<T, M, N> {
173 fn default() -> Self {
174 Self {
175 tag_matcher: M::default(),
176 next: N::default(),
177 trace: None,
178 entered: None,
179 }
180 }
181}
182
183impl<T, M, N> ElementEnterDeserialize<T, M, N> {
184 pub fn new(tag_matcher: M, next: N) -> Self {
186 Self { tag_matcher, next, trace: None, entered: None }
187 }
188
189 pub fn set_trace(&mut self, f: impl FnMut(&T) + 'static) {
191 self.trace = Some(Box::new(f) as Box<_>);
192 }
193}
194
195impl<T, N> ElementEnterDeserialize<T, ExactTagMatch, N> {
196 pub fn tag(tag: &'static str, next: N) -> Self {
198 Self::new(ExactTagMatch { needle: tag }, next)
199 }
200}
201
202impl<T, N> ElementEnterDeserialize<T, AnyTagMatch, N> {
203 pub fn any(next: N) -> Self {
205 Self::new(AnyTagMatch {}, next)
206 }
207}
208
209#[derive(Debug, PartialEq)]
216pub struct ElementDeserialize<T: DeserializeOwned, M> {
217 tag_matcher: M,
218 _phantom: PhantomData<T>,
219}
220
221impl<T: DeserializeOwned, M: Default> Default for ElementDeserialize<T, M> {
222 fn default() -> Self {
223 Self {
224 tag_matcher: M::default(),
225 _phantom: PhantomData,
226 }
227 }
228}
229
230impl<T: DeserializeOwned, M> ElementDeserialize<T, M> {
231 pub fn new(tag_matcher: M) -> Self {
233 Self { tag_matcher, _phantom: PhantomData }
234 }
235}
236
237impl<T: DeserializeOwned> ElementDeserialize<T, ExactTagMatch> {
238 pub fn tag(tag: &'static str) -> Self {
240 Self::new(ExactTagMatch { needle: tag })
241 }
242}
243
244impl<T: DeserializeOwned> ElementDeserialize<T, AnyTagMatch> {
245 pub fn any() -> Self {
247 Self::new(AnyTagMatch {})
248 }
249}
250
251mod private {
252 use serde::de::DeserializeOwned;
253
254 pub trait Sealed {}
255
256 impl<N, M> Sealed for super::ElementEnter<N, M> {}
257 impl<T: DeserializeOwned, N, M> Sealed for super::ElementEnterDeserialize<T, N, M> {}
258 impl<T: DeserializeOwned, M> Sealed for super::ElementDeserialize<T, M> {}
259}
260
261#[doc(hidden)]
262pub trait XmlPath: private::Sealed {
263 type Output: DeTuple;
264
265 fn go<R: Read>(&mut self, parser: &mut Parser<R>) -> Option<Result<Self::Output, DeserializeError>>;
266}
267
268impl<M: TagMatcher, N: XmlPath> XmlPath for ElementEnter<M, N> {
269 type Output = N::Output;
270
271 fn go<R: Read>(&mut self, parser: &mut Parser<R>) -> Option<Result<Self::Output, DeserializeError>> {
272 loop {
273 if self.entered {
274 if let Some(out) = self.next.go(parser) {
275 return Some(out);
276 }
277 }
278 self.entered = false;
279
280 let mut event = try_some!(parser.next());
281 match event.code() {
282 EventCode::StartTag => {
283 let tag_name = try_some!(event.get_str());
284 if self.tag_matcher.matches(tag_name.as_ref()) {
285 self.entered = true;
286 } else {
287 try_some!(parser.finish_tag(1));
288 }
289 },
290 EventCode::EndTagImmediate | EventCode::EndTag | EventCode::Eof => {
291 return None;
292 },
293 EventCode::AttributeName | EventCode::AttributeValue | EventCode::Text => {}
294 }
295 }
296 }
297}
298
299impl<T: DeserializeOwned + Clone, M: TagMatcher, N: XmlPath> XmlPath for ElementEnterDeserialize<T, M, N>
300 where N::Output: Prepend<T>, <N::Output as Prepend<T>>::Output: DeTuple
301{
302 type Output = <N::Output as Prepend<T>>::Output;
303
304 fn go<R: Read>(&mut self, parser: &mut Parser<R>) -> Option<Result<Self::Output, DeserializeError>> {
305 loop {
306 if let Some(entered) = &self.entered {
307 if let Some(out) = self.next.go(parser) {
308 return match out {
309 Ok(out) => {
310 Some(Ok(out.prepend((*entered).clone())))
314 }
315 Err(err) => Some(Err(err))
316 };
317 }
318 }
319 self.entered = None;
320
321 let mut event = try_some!(parser.next());
322 match event.code() {
323 EventCode::StartTag => {
324 let tag_name = try_some!(event.get_str());
325 if self.tag_matcher.matches(&tag_name) {
326 let opening_tag = tag_name.into();
327 let mut des = Deserializer::new_inside_tag(parser, opening_tag, true);
328 self.entered = Some(try_some!(T::deserialize(&mut des)));
329 if let Some(f) = self.trace.as_mut() {
330 f(self.entered.as_ref().unwrap());
331 }
332 if let EventCode::EndTag | EventCode::EndTagImmediate = try_some!(parser.peek()).code() {
333 let _ = parser.next().unwrap();
337 self.entered = None;
338 }
339 } else {
340 try_some!(parser.finish_tag(1));
341 }
342 },
343 EventCode::EndTagImmediate | EventCode::EndTag | EventCode::Eof => {
344 return None;
345 },
346 EventCode::AttributeName | EventCode::AttributeValue | EventCode::Text => {}
347 }
348 }
349 }
350}
351
352impl<T: DeserializeOwned, M: TagMatcher> XmlPath for ElementDeserialize<T, M> {
353 type Output = (T,);
354
355 fn go<R: Read>(&mut self, parser: &mut Parser<R>) -> Option<Result<Self::Output, DeserializeError>> {
356 loop {
357 let mut event = try_some!(parser.next());
358 match event.code() {
359 EventCode::StartTag => {
360 let tag_name = try_some!(event.get_str());
361 if self.tag_matcher.matches(tag_name.as_ref()) {
362 let opening_tag = tag_name.into();
363 let mut des = Deserializer::new_inside_tag(parser, opening_tag, false);
364 return Some(Ok((try_some!(T::deserialize(&mut des)), )))
365 }
366 },
367 EventCode::EndTagImmediate | EventCode::EndTag => {
368 return None;
369 },
370 EventCode::Eof => {
371 return Some(Err(DeserializeError::UnexpectedEof));
372 },
373 EventCode::AttributeName | EventCode::AttributeValue | EventCode::Text => {}
374 }
375 }
376 }
377}
378
379pub struct TreeDeserializer<R: Read, N> {
389 parser: Parser<R>,
390 path: N,
391}
392
393pub type TreeDeserializerOutput<N> = <<N as XmlPath>::Output as DeTuple>::Output;
395
396impl<R: Read, N: XmlPath> TreeDeserializer<R, N> {
397 pub fn from_path(path: N, parser: Parser<R>) -> Self {
399 Self {
400 parser,
401 path,
402 }
403 }
404
405 pub fn from_path_and_reader(path: N, reader: R) -> Self {
407 Self::from_path(path, Parser::new(reader))
408 }
409}
410
411impl<R: Read, N: XmlPath + Default> TreeDeserializer<R, N> {
412 pub fn new(parser: Parser<R>) -> Self {
414 Self {
415 parser,
416 path: N::default(),
417 }
418 }
419
420 pub fn from_reader(reader: R) -> Self {
422 Self::new(Parser::new(reader))
423 }
424}
425
426impl<R: Read, N: XmlPath> Iterator for TreeDeserializer<R, N> where N::Output: DeTuple {
427 type Item = Result<<N::Output as DeTuple>::Output, DeserializeError>;
428
429 fn next(&mut self) -> Option<Self::Item> {
430 match self.path.go(&mut self.parser) {
431 Some(Ok(tuple)) => Some(Ok(tuple.detuple())),
432 Some(Err(err)) => Some(Err(err)),
433 None => None,
434 }
435 }
436}
437
438#[macro_export]
464macro_rules! xml_path {
465 ($tag_name:literal => $t:ty) => {
467 $crate::tree::ElementDeserialize::<$t, _>::tag($tag_name)
468 };
469 (* => $t:ty) => {
470 $crate::tree::ElementDeserialize::<$t, _>::any()
471 };
472
473 ($tag_name:literal) => { $crate::xml_path!(*) };
476 (*) => { compile_error!("Paths must end with `\"tag_name\" => Type` expression.") };
477
478 ($tag_name:literal => $t:ty, $($r_tag_name:tt $(=> $r_t:ty)?),+) => {
481 $crate::tree::ElementEnterDeserialize::<$t, _, _>::tag($tag_name,
482 $crate::xml_path!($($r_tag_name $(=> $r_t)?),+)
483 )
484 };
485 (* => $t:ty, $($r_tag_name:tt $(=> $r_t:ty)?),+) => {
486 $crate::tree::ElementEnterDeserialize::<$t, _, _>::any(
487 $crate::xml_path!($($r_tag_name $(=> $r_t)?),+)
488 )
489 };
490
491 ($tag_name:literal, $($r_tag_name:tt $(=> $r_t:ty)?),+) => {
494 $crate::tree::ElementEnter::tag($tag_name,
495 $crate::xml_path!($($r_tag_name $(=> $r_t)?),+)
496 )
497 };
498 (*, $($r_tag_name:tt $(=> $r_t:ty)?),+) => {
499 $crate::tree::ElementEnter::any(
500 $crate::xml_path!($($r_tag_name $(=> $r_t)?),+)
501 )
502 };
503}
504
505#[macro_export]
526macro_rules! xml_path_type {
527 ($type_name:ident : $($r_tag_name:tt $(=> $r_t:ty)?),+) => {
547 $crate::tree::paste_item! {
548 #[allow(non_snake_case)]
549 #[doc(hidden)]
550 mod [<$type_name __rapid_xml_generated_matchers>] {
551 $crate::xml_path_type!(@structs A $($r_tag_name $(=> $r_t)?),+);
552 }
553 }
554
555 type $type_name = $crate::xml_path_type!(@types $type_name A $($r_tag_name $(=> $r_t)?),+);
556 };
557
558 (@structs $suffix:ident $tag_name:literal $(=> $t:ty)?) => {
565 $crate::tree::paste_item! {
566 #[derive(Debug, Default, PartialEq)]
567 pub struct [<$($t)? Matcher $suffix>] {}
568
569 impl $crate::tree::TagMatcher for [<$($t)? Matcher $suffix>] {
570 #[inline(always)]
571 fn matches(&self, tag_name: &str) -> bool {
572 tag_name == $tag_name
573 }
574 }
575 }
576 };
577 (@structs $suffix:ident * $(=> $t:ty)?) => {
579 };
581
582 (@structs $suffix:ident $tag_name:literal $(=> $t:ty)?, $($r_tag_name:tt $(=> $r_t:ty)?),+) => {
586 $crate::tree::paste_item! {
587 $crate::xml_path_type!(@structs $suffix $tag_name $(=> $t)?);
588 $crate::xml_path_type!(@structs [<$suffix A>] $($r_tag_name $(=> $r_t)?),*);
589 }
590 };
591
592 (@structs $suffix:ident * $(=> $t:ty)?, $($r_tag_name:tt $(=> $r_t:ty)?),+) => {
595 $crate::tree::paste_item! {
596 $crate::xml_path_type!(@structs [<$suffix A>] $($r_tag_name $(=> $r_t)?),+);
597 }
598 };
599
600 (@types $type_name:ident $suffix:ident $tag_name:literal => $t:ty) => {
607 $crate::tree::paste_item! {
608 $crate::tree::ElementDeserialize<$t, [<$type_name __rapid_xml_generated_matchers>]::[<$t Matcher $suffix>]>
609 }
610 };
611 (@types $type_name:ident $suffix:ident * => $t:ty) => {
612 $crate::tree::ElementDeserialize::<$t, $crate::tree::AnyTagMatch>
613 };
614
615 (@types $type_name:ident $suffix:ident $tag_name:literal) => { $crate::xml_path_type!(*) };
618 (@types $type_name:ident $suffix:ident *) => { compile_error!("Paths must end with `\"tag_name\" => Type` expression.") };
619
620 (@types $type_name:ident $suffix:ident $tag_name:literal => $t:ty, $($r_tag_name:tt $(=> $r_t:ty)?),+) => {
623 $crate::tree::paste_item! {
624 $crate::tree::ElementEnterDeserialize<$t, [<$type_name __rapid_xml_generated_matchers>]::[<$t Matcher $suffix>],
625 $crate::xml_path_type!(@types $type_name [<$suffix A>] $($r_tag_name $(=> $r_t)?),+)
626 >
627 }
628 };
629 (@types $type_name:ident $suffix:ident * => $t:ty, $($r_tag_name:tt $(=> $r_t:ty)?),+) => {
630 $crate::tree::paste_item! {
631 $crate::tree::ElementEnterDeserialize::<$t, $crate::tree::AnyTagMatch,
632 $crate::xml_path_type!(@types $type_name [<$suffix A>] $($r_tag_name $(=> $r_t)?),+)
633 >
634 }
635 };
636
637 (@types $type_name:ident $suffix:ident $tag_name:literal, $($r_tag_name:tt $(=> $r_t:ty)?),+) => {
640 $crate::tree::paste_item! {
641 $crate::tree::ElementEnter<[<$type_name __rapid_xml_generated_matchers>]::[<Matcher $suffix>],
642 $crate::xml_path_type!(@types $type_name [<$suffix A>] $($r_tag_name $(=> $r_t)?),+)
643 >
644 }
645 };
646 (@types $type_name:ident $suffix:ident *, $($r_tag_name:tt $(=> $r_t:ty)?),+) => {
647 $crate::tree::paste_item! {
648 $crate::tree::ElementEnter::<$crate::tree::AnyTagMatch,
649 $crate::xml_path_type!(@types $type_name [<$suffix A>] $($r_tag_name $(=> $r_t)?),+)
650 >
651 }
652 };
653}
654
655#[cfg(test)]
656mod tests {
657 use std::io::Cursor;
658
659 use serde_derive::Deserialize;
660
661 use super::*;
662
663 #[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
664 struct Root {
665 xyz: u32,
666 }
667
668 #[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
669 struct Bbb {
670 n: u32,
671 }
672
673 #[derive(Debug, Deserialize, PartialEq, Eq)]
674 struct Ccc {
675 m: u32,
676 }
677
678 const SAMPLE_XML: &[u8] = br#"
679 <root xyz="42">
680 <aaa>
681 <bbb n="1">
682 <ccc m="100"/>
683 <ccc m="200"/>
684 </bbb>
685 <xxx>Unknown tag</xxx>
686 </aaa>
687 <xxx>Unknown tag</xxx>
688 <aaa>
689 <bbb n="99"/>
690 </aaa>
691 <aaa/>
692 <aaa>
693 <bbb n="99">Matched tag without anything nested</bbb>
694 <bbb n="2">
695 <ccc><m>300</m></ccc>
696 <ccc><m>400</m></ccc>
697 </bbb>
698 </aaa>
699 <aaa2>
700 <bbb n="3">
701 <ccc m="500"/>
702 </bbb>
703 </aaa2>
704 </root>
705 "#;
706
707 const SAMPLE_XML_ERRORS: &[u8] = br#"
708 <root xyz="42">
709 <aaa>
710 <bbb n="1">
711 <ccc m="100"/>
712 <ccc/>
713 <ccc m="200"/>
714 <ccc m=250/>
715 </bbb>
716 <xxx>Unknown tag</xxx>
717 </aaa>
718 <xxx>Unknown tag</xxx>
719 <aaa>
720 <bbb n="99">Matched tag without anything nested</bbb>
721 <bbb n="2">
722 <ccc><m>300</m></ccc>
723 <ccc><m>asdf</m></ccc>
724 </bbb>
725 </aaa>
726 </root>
727 "#;
728
729 #[test]
730 fn basic() {
731 let path = xml_path!("root", "aaa", "bbb", "ccc" => Ccc);
732
733 let mut des = TreeDeserializer::from_path_and_reader(path, Cursor::new(&SAMPLE_XML[..]));
734
735 assert_eq!(des.next().unwrap().unwrap(), Ccc { m: 100 });
736 assert_eq!(des.next().unwrap().unwrap(), Ccc { m: 200 });
737 assert_eq!(des.next().unwrap().unwrap(), Ccc { m: 300 });
738 assert_eq!(des.next().unwrap().unwrap(), Ccc { m: 400 });
739 assert!(des.next().is_none());
740 }
741
742 #[test]
743 fn basic_100_times() {
744 let path = xml_path!("root", "aaa", "bbb", "ccc" => Ccc);
745
746 let xml = SAMPLE_XML.repeat(100);
747 let mut des = TreeDeserializer::from_path_and_reader(path, Cursor::new(&xml));
748
749 for _ in 0..100 {
750 assert_eq!(des.next().unwrap().unwrap(), Ccc { m: 100 });
751 assert_eq!(des.next().unwrap().unwrap(), Ccc { m: 200 });
752 assert_eq!(des.next().unwrap().unwrap(), Ccc { m: 300 });
753 assert_eq!(des.next().unwrap().unwrap(), Ccc { m: 400 });
754 }
755 assert!(des.next().is_none());
756 }
757
758 #[test]
759 fn wildcard() {
760 let path = xml_path!("root", *, "bbb", "ccc" => Ccc);
761
762 let mut des = TreeDeserializer::from_path_and_reader(path, Cursor::new(&SAMPLE_XML[..]));
763
764 assert_eq!(des.next().unwrap().unwrap(), Ccc { m: 100 });
765 assert_eq!(des.next().unwrap().unwrap(), Ccc { m: 200 });
766 assert_eq!(des.next().unwrap().unwrap(), Ccc { m: 300 });
767 assert_eq!(des.next().unwrap().unwrap(), Ccc { m: 400 });
768 assert_eq!(des.next().unwrap().unwrap(), Ccc { m: 500 });
769 assert!(des.next().is_none());
770 }
771
772 #[test]
773 fn multiple_elements() {
774 let path = xml_path!("root", "aaa", "bbb" => Bbb, "ccc" => Ccc);
775
776 let mut des = TreeDeserializer::from_path_and_reader(path, Cursor::new(&SAMPLE_XML[..]));
777
778 assert_eq!(des.next().unwrap().unwrap(), (Bbb { n: 1 }, Ccc { m: 100 }));
779 assert_eq!(des.next().unwrap().unwrap(), (Bbb { n: 1 }, Ccc { m: 200 }));
780 assert_eq!(des.next().unwrap().unwrap(), (Bbb { n: 2 }, Ccc { m: 300 }));
781 assert_eq!(des.next().unwrap().unwrap(), (Bbb { n: 2 }, Ccc { m: 400 }));
782 assert!(des.next().is_none());
783 }
784
785 xml_path_type!(MyPath: "root", "aaa", "bbb" => Bbb, "ccc" => Ccc);
786
787 #[test]
788 fn multiple_elements_with_type() {
789 let mut des = TreeDeserializer::<_, MyPath>::from_reader(Cursor::new(&SAMPLE_XML[..]));
790
791 assert_eq!(des.next().unwrap().unwrap(), (Bbb { n: 1 }, Ccc { m: 100 }));
792 assert_eq!(des.next().unwrap().unwrap(), (Bbb { n: 1 }, Ccc { m: 200 }));
793 assert_eq!(des.next().unwrap().unwrap(), (Bbb { n: 2 }, Ccc { m: 300 }));
794 assert_eq!(des.next().unwrap().unwrap(), (Bbb { n: 2 }, Ccc { m: 400 }));
795 assert!(des.next().is_none());
796 }
797
798 #[test]
799 fn with_errors() {
800 let path = xml_path!("root", "aaa", "bbb" => Bbb, "ccc" => Ccc);
801
802 let mut des = TreeDeserializer::from_path_and_reader(path, Cursor::new(&SAMPLE_XML_ERRORS[..]));
803
804 assert_eq!(des.next().unwrap().unwrap(), (Bbb { n: 1 }, Ccc { m: 100 }));
807 assert!(des.next().unwrap().is_err());
808 assert_eq!(des.next().unwrap().unwrap(), (Bbb { n: 1 }, Ccc { m: 200 }));
809 assert!(des.next().unwrap().is_err()); assert_eq!(des.next().unwrap().unwrap(), (Bbb { n: 2 }, Ccc { m: 300 }));
811 assert!(des.next().unwrap().is_err());
812 assert!(des.next().is_none());
813 }
814
815 #[test]
816 fn parse_root() {
817 let path = xml_path!("root" => Root, "aaa", "bbb", "ccc" => Ccc);
818
819 let mut des = TreeDeserializer::from_path_and_reader(path, Cursor::new(&SAMPLE_XML[..]));
820
821 assert_eq!(des.next().unwrap().unwrap(), (Root { xyz: 42, }, Ccc { m: 100 }));
822 assert_eq!(des.next().unwrap().unwrap(), (Root { xyz: 42, }, Ccc { m: 200 }));
823 assert_eq!(des.next().unwrap().unwrap(), (Root { xyz: 42, }, Ccc { m: 300 }));
824 assert_eq!(des.next().unwrap().unwrap(), (Root { xyz: 42, }, Ccc { m: 400 }));
825 assert!(des.next().is_none());
826 }
827}
828
829#[cfg(test)]
830#[cfg(feature = "bencher")]
831mod bench {
832 use std::io::Cursor;
833 use test::{Bencher, black_box};
834
835 use serde_derive::Deserialize;
836
837 use super::*;
838
839 #[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
840 pub enum Thing {
841 VariantA {
842 some_field: String,
843 },
844
845 VariantB {
846 some_field: String,
847 another_field: Option<u32>,
848 abcd_efgh: Option<u32>,
849 mumble_rumble: i16,
850 short: Option<String>,
851 field_with_long_name: Option<u32>,
852 xyz_qwerty: Option<u32>,
853 },
854
855 VariantC {
856 some_field: String,
857 field_1: u32,
858 field_2: i16,
859 field_3: u32,
860 },
861 }
862
863 const BENCH_XML: &[u8] = br#"<root><group>
864<VariantA some_field="TextAbcd"/>
865<VariantB some_field="TextAbcd"><another_field>80</another_field><abcd_efgh>4587</abcd_efgh><mumble_rumble>-8</mumble_rumble><short>AnotherText</short><field_with_long_name>79452</field_with_long_name></VariantB>
866<VariantC some_field="TextAbcd"><field_1>123</field_1><field_2>-3</field_2><field_3>567</field_3></VariantC>
867</group></root>"#;
868
869 #[bench]
870 fn bench_tree_deserializer(b: &mut Bencher) {
871 let xml = BENCH_XML.repeat(10000);
872
873 b.iter(move || {
874 let path = xml_path!("root", "group", * => Thing);
875 let mut des = TreeDeserializer::from_path_and_reader(path, Cursor::new(&xml));
876 while let Some(item) = des.next() {
877 black_box(item.unwrap());
878 }
879 });
880 }
881}