forked from lavina/lavina
				
			generator-based parsing of xmpp stanzas
This commit is contained in:
		
							parent
							
								
									bba1ea107d
								
							
						
					
					
						commit
						d0f807841c
					
				|  | @ -0,0 +1 @@ | ||||||
|  | nightly | ||||||
|  | @ -1,3 +1,5 @@ | ||||||
|  | #![feature(generators, generator_trait, type_alias_impl_trait)] | ||||||
|  | 
 | ||||||
| mod core; | mod core; | ||||||
| mod prelude; | mod prelude; | ||||||
| mod projections; | mod projections; | ||||||
|  |  | ||||||
|  | @ -18,58 +18,25 @@ pub enum IqClientBody { | ||||||
|     Unknown(Ignore), |     Unknown(Ignore), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(From)] |  | ||||||
| pub struct IqClientBodyParser(IqClientBodyParserInner); |  | ||||||
| 
 |  | ||||||
| #[derive(From)] |  | ||||||
| enum IqClientBodyParserInner { |  | ||||||
|     Initial, |  | ||||||
|     Bind(<BindRequest as FromXml>::P), |  | ||||||
|     SessionV(<Session as FromXml>::P), |  | ||||||
|     RosterV(<RosterQuery as FromXml>::P), |  | ||||||
|     UnknownV(<Ignore as FromXml>::P), |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl FromXml for IqClientBody { | impl FromXml for IqClientBody { | ||||||
|     type P = IqClientBodyParser; |     type P = impl Parser<Output = Result<Self>>; | ||||||
| 
 | 
 | ||||||
|     fn parse() -> Self::P { |     fn parse() -> Self::P { | ||||||
|         IqClientBodyParserInner::Initial.into() |         |(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result<Self> { | ||||||
|     } |             let bytes = match event { | ||||||
| } |                 Event::Start(bytes) => bytes, | ||||||
| 
 |                 Event::Empty(bytes) => bytes, | ||||||
| impl Parser for IqClientBodyParser { |                 _ => return Err(ffail!("Unexpected XML event: {event:?}")), | ||||||
|     type Output = Result<IqClientBody>; |             }; | ||||||
| 
 |             let name = bytes.name(); | ||||||
|     fn consume<'a>( |             match_parser!(name, namespace, event; | ||||||
|         self: Self, |                 BindRequest, | ||||||
|         namespace: quick_xml::name::ResolveResult, |                 Session, | ||||||
|         event: &quick_xml::events::Event<'a>, |                 RosterQuery, | ||||||
|     ) -> crate::util::xml::Continuation<Self, Self::Output> { |                 { | ||||||
|         use IqClientBodyParserInner::*; |                     delegate_parsing!(Ignore, namespace, event).into() | ||||||
|         match self.0 { |                 } | ||||||
|             Initial => { |             ) | ||||||
|                 let bytes = match event { |  | ||||||
|                     Event::Start(bytes) => bytes, |  | ||||||
|                     Event::Empty(bytes) => bytes, |  | ||||||
|                     _ => { |  | ||||||
|                         return Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))) |  | ||||||
|                     } |  | ||||||
|                 }; |  | ||||||
|                 let name = bytes.name(); |  | ||||||
|                 match_parser!(IqClientBodyParser, name, namespace, event; |  | ||||||
|                     BindRequest, |  | ||||||
|                     Session, |  | ||||||
|                     RosterQuery, |  | ||||||
|                     { |  | ||||||
|                         IqClientBodyParser(Ignore::parse().into()).consume(namespace, event) |  | ||||||
|                     } |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
|             Bind(p) => delegate_parsing!(p, IqClientBodyParserInner, namespace, event), |  | ||||||
|             SessionV(p) => delegate_parsing!(p, IqClientBodyParserInner, namespace, event), |  | ||||||
|             RosterV(p) => delegate_parsing!(p, IqClientBodyParserInner, namespace, event), |  | ||||||
|             UnknownV(p) => delegate_parsing!(p, IqClientBodyParserInner, namespace, event), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -81,52 +48,27 @@ pub enum ClientPacket { | ||||||
|     Presence(Presence<Ignore>), |     Presence(Presence<Ignore>), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(From)] |  | ||||||
| pub struct ClientPacketParser(ClientPacketParserInner); |  | ||||||
| 
 |  | ||||||
| impl FromXml for ClientPacket { | impl FromXml for ClientPacket { | ||||||
|     type P = ClientPacketParser; |     type P = impl Parser<Output = Result<Self>>; | ||||||
| 
 | 
 | ||||||
|     fn parse() -> Self::P { |     fn parse() -> Self::P { | ||||||
|         ClientPacketParserInner::Initial.into() |         |(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result<Self> { | ||||||
|     } |             let Event::Start(bytes) = event else { | ||||||
| } |                 return Err(ffail!("Unexpected XML event: {event:?}")); | ||||||
| 
 |             }; | ||||||
| #[derive(From)] |             let name = bytes.name(); | ||||||
| enum ClientPacketParserInner { |             match_parser!(name, namespace, event; | ||||||
|     Initial, |                 Iq::<IqClientBody>, | ||||||
|     IqV(<Iq<IqClientBody> as FromXml>::P), |                 Presence::<Ignore>, | ||||||
|     MessageV(<Message as FromXml>::P), |                 Message, | ||||||
|     PresenceV(<Presence<Ignore> as FromXml>::P), |                 { | ||||||
| } |                     Err(ffail!( | ||||||
| 
 |                         "Unexpected XML event of name {:?} in namespace {:?}", | ||||||
| impl Parser for ClientPacketParser { |                         name, | ||||||
|     type Output = Result<ClientPacket>; |                         namespace | ||||||
| 
 |                     )) | ||||||
|     fn consume<'a>( |                 } | ||||||
|         self: Self, |             ) | ||||||
|         namespace: ResolveResult, |  | ||||||
|         event: &Event<'a>, |  | ||||||
|     ) -> Continuation<Self, Self::Output> { |  | ||||||
|         use ClientPacketParserInner::*; |  | ||||||
|         match self.0 { |  | ||||||
|             Initial => { |  | ||||||
|                 let Event::Start(bytes) = event else { |  | ||||||
|                     return Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))); |  | ||||||
|                 }; |  | ||||||
|                 let name = bytes.name(); |  | ||||||
|                 match_parser!(ClientPacketParser, name, namespace, event; |  | ||||||
|                     Iq::<IqClientBody>, |  | ||||||
|                     Presence::<Ignore>, |  | ||||||
|                     Message, |  | ||||||
|                     { |  | ||||||
|                         Continuation::Final(Err(ffail!("Unexpected XML event of name {:?} in namespace {:?}", name, namespace))) |  | ||||||
|                     } |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
|             IqV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event), |  | ||||||
|             MessageV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event), |  | ||||||
|             PresenceV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,3 +1,6 @@ | ||||||
|  | use std::ops::Generator; | ||||||
|  | use std::pin::Pin; | ||||||
|  | 
 | ||||||
| use quick_xml::events::Event; | use quick_xml::events::Event; | ||||||
| use quick_xml::name::ResolveResult; | use quick_xml::name::ResolveResult; | ||||||
| 
 | 
 | ||||||
|  | @ -31,6 +34,30 @@ pub trait Parser: Sized { | ||||||
|     ) -> Continuation<Self, Self::Output>; |     ) -> Continuation<Self, Self::Output>; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | impl<T, Out> 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<Self, Self::Output> { | ||||||
|  |         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<Event<'a>>)
 | ||||||
|  |         // 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<Parser, Res> { | pub enum Continuation<Parser, Res> { | ||||||
|     Final(Res), |     Final(Res), | ||||||
|     Continue(Parser), |     Continue(Parser), | ||||||
|  | @ -46,31 +73,34 @@ macro_rules! fail_fast { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| macro_rules! delegate_parsing { | macro_rules! delegate_parsing { | ||||||
|     ($parser: ident, $intermediate: ident, $namespace: expr, $event: expr) => { |     ($parser: ty, $namespace: expr, $event: expr) => {{ | ||||||
|         match $parser.consume($namespace, $event) { |         let mut parser = <$parser as FromXml>::parse().consume($namespace, $event); | ||||||
|             Continuation::Final(Ok(r)) => Continuation::Final(Ok(r.into())), |         loop { | ||||||
|             Continuation::Final(Err(e)) => Continuation::Final(Err(e)), |             match parser { | ||||||
|             Continuation::Continue(s) => { |                 Continuation::Final(Ok(res)) => break Ok(res.into()), | ||||||
|                 let inner: $intermediate = s.into(); |                 Continuation::Final(Err(err)) => break Err(err), | ||||||
|                 Continuation::Continue(inner.into()) |                 Continuation::Continue(p) => { | ||||||
|  |                     let (namespace, event) = yield; | ||||||
|  |                     parser = p.consume(namespace, event); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     }; |     }}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| macro_rules! match_parser { | macro_rules! match_parser { | ||||||
|     ($outer: ident, $name: expr, $ns: expr, $event: expr; $subtype: ty, $fin: block) => { |     ($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())) { |         if $name.0 == <$subtype as FromXmlTag>::NAME.as_bytes() && $ns == ResolveResult::Bound(Namespace(<$subtype as FromXmlTag>::NS.as_bytes())) { | ||||||
|             $outer(<$subtype as FromXml>::parse().into()).consume($ns, $event) |             delegate_parsing!($subtype, $ns, $event) | ||||||
|         } else { |         } else { | ||||||
|             $fin |             $fin | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     ($outer: ident, $name: expr, $ns: expr, $event: expr; $subtype: ty, $($rest: ty),+, $fin: block) => { |     ($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())) { |         if $name.0 == <$subtype as FromXmlTag>::NAME.as_bytes() && $ns == ResolveResult::Bound(Namespace(<$subtype as FromXmlTag>::NS.as_bytes())) { | ||||||
|             $outer(<$subtype as FromXml>::parse().into()).consume($ns, $event) |             delegate_parsing!($subtype, $ns, $event) | ||||||
|         } else { |         } else { | ||||||
|             match_parser!($outer, $name, $ns, $event; $($rest),*, $fin) |             match_parser!($name, $ns, $event; $($rest),*, $fin) | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue