use std::ops::Generator; use std::pin::Pin; use quick_xml::events::Event; use quick_xml::name::ResolveResult; use crate::prelude::Result; mod ignore; pub use ignore::Ignore; pub trait FromXml: Sized { type P: Parser>; fn parse() -> Self::P; } pub trait ToXml: Sized { fn serialize(&self, events: &mut Vec>); } pub trait FromXmlTag: FromXml { const NAME: &'static str; const NS: &'static str; } pub trait Parser: Sized { type Output; fn consume<'a>( self: Self, namespace: ResolveResult, event: &Event<'a>, ) -> Continuation; } impl Parser for T where T: Generator<(ResolveResult<'static>, &'static Event<'static>), Yield = (), Return = Out> + Unpin, { type Output = Out; fn consume<'a>( mut self: Self, namespace: ResolveResult, event: &Event<'a>, ) -> Continuation { let s = Pin::new(&mut self); // this is a very rude workaround fixing the fact that rust generators // 1. don't support higher-kinded lifetimes (i.e. no `impl for <'a> Generator>) // 2. don't track borrows across yield points and lack thereof // implementors of Parser should manually check that inputs are not used across yields match s.resume(unsafe { std::mem::transmute((namespace, event)) }) { std::ops::GeneratorState::Yielded(()) => Continuation::Continue(self), std::ops::GeneratorState::Complete(res) => Continuation::Final(res), } } } pub enum Continuation { Final(Res), Continue(Parser), } macro_rules! fail_fast { ($errorable: expr) => { match $errorable { Ok(i) => i, Err(e) => return Continuation::Final(Err(e.into())), } }; } macro_rules! delegate_parsing { ($parser: ty, $namespace: expr, $event: expr) => {{ let mut parser = <$parser as FromXml>::parse().consume($namespace, $event); loop { match parser { Continuation::Final(Ok(res)) => break Ok(res.into()), Continuation::Final(Err(err)) => break Err(err), Continuation::Continue(p) => { let (namespace, event) = yield; parser = p.consume(namespace, event); } } } }}; } macro_rules! match_parser { ($name: expr, $ns: expr, $event: expr; $subtype: ty, $fin: block) => { if $name.0 == <$subtype as FromXmlTag>::NAME.as_bytes() && $ns == ResolveResult::Bound(Namespace(<$subtype as FromXmlTag>::NS.as_bytes())) { delegate_parsing!($subtype, $ns, $event) } else { $fin } }; ($name: expr, $ns: expr, $event: expr; $subtype: ty, $($rest: ty),+, $fin: block) => { if $name.0 == <$subtype as FromXmlTag>::NAME.as_bytes() && $ns == ResolveResult::Bound(Namespace(<$subtype as FromXmlTag>::NS.as_bytes())) { delegate_parsing!($subtype, $ns, $event) } else { match_parser!($name, $ns, $event; $($rest),*, $fin) } }; } pub(crate) use delegate_parsing; pub(crate) use fail_fast; pub(crate) use match_parser;