From 372efc370162454ceabbf039cb883d8bfd73a00c Mon Sep 17 00:00:00 2001 From: Nikita Vilunov Date: Tue, 19 Sep 2023 22:11:43 +0200 Subject: [PATCH] xmpp: rewrite bind request parser as a coroutine --- src/protos/xmpp/bind.rs | 112 ++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 72 deletions(-) diff --git a/src/protos/xmpp/bind.rs b/src/protos/xmpp/bind.rs index 35d0dfc..4186049 100644 --- a/src/protos/xmpp/bind.rs +++ b/src/protos/xmpp/bind.rs @@ -78,89 +78,57 @@ impl Jid { #[derive(PartialEq, Eq, Debug)] pub struct BindRequest(pub Resource); -pub struct BindRequestParser(BindRequestParserInner); - -enum BindRequestParserInner { - Initial, - /// Consumed start and expects - InBind(Option), - /// Consumed start - InBindResourceInitial, - /// Consumer start and inner text - InBindResourceEnd(String), -} - impl FromXmlTag for BindRequest { const NS: &'static str = XMLNS; const NAME: &'static str = "bind"; } impl FromXml for BindRequest { - type P = BindRequestParser; + type P = impl Parser>; fn parse() -> Self::P { - BindRequestParser(BindRequestParserInner::Initial) - } -} - -// TODO rewrite as a generator -impl Parser for BindRequestParser { - type Output = Result; - - fn consume<'a>( - self: Self, - namespace: ResolveResult, - event: &Event<'a>, - ) -> Continuation { - // TODO validate tag names and namespaces - use BindRequestParserInner::*; - match self.0 { - Initial => { - let Event::Start(bytes) = event else { - return Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))); - }; - if bytes.name().0 != BindRequest::NAME.as_bytes() { - return Continuation::Final(Err(ffail!( - "Unexpected XML tag: {:?}", - bytes.name() - ))); - } - let ResolveResult::Bound(Namespace(ns)) = namespace else { - return Continuation::Final(Err(ffail!("No namespace provided"))); - }; - if ns != XMLNS.as_bytes() { - return Continuation::Final(Err(ffail!("Incorrect namespace"))); - } - Continuation::Continue(BindRequestParser(InBind(None))) + |(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result { + let mut resource: Option> = None; + let Event::Start(bytes) = event else { + return Err(ffail!("Unexpected XML event: {event:?}")); + }; + if bytes.name().0 != BindRequest::NAME.as_bytes() { + return Err(ffail!("Unexpected XML tag: {:?}", bytes.name())); } - InBind(resource) => match event { - Event::Start(bytes) => { - Continuation::Continue(BindRequestParser(InBindResourceInitial)) - } - Event::End(bytes) => { - let Some(resource) = resource else { - return Continuation::Final(Err(ffail!("No resource was provided"))); - }; - Continuation::Final(Ok(BindRequest(Resource(resource.into())))) - } - _ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))), - }, - InBindResourceInitial => { - let Event::Text(text) = event else { - return Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))); - }; - let resource = match std::str::from_utf8(text.as_bytes()) { - Ok(e) => e.to_string(), - Err(err) => return Continuation::Final(Err(err.into())), - }; - Continuation::Continue(BindRequestParser(InBindResourceEnd(resource))) + let ResolveResult::Bound(Namespace(ns)) = namespace else { + return Err(ffail!("No namespace provided")); + }; + if ns != XMLNS.as_bytes() { + return Err(ffail!("Incorrect namespace")); } - InBindResourceEnd(resource) => { - let Event::End(bytes) = event else { - return Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))); - }; - Continuation::Continue(BindRequestParser(InBind(Some(resource)))) + loop { + let (namespace, event) = yield; + match event { + Event::Start(bytes) if bytes.name().0 == b"resource" => { + let (namespace, event) = yield; + if let Event::Text(text) = event { + resource = Some(text.as_bytes().into()); + } + let (namespace, event) = yield; + if let Event::End(_) = event { + } else { + return Err(ffail!("Unexpected XML event: {event:?}")); + } + } + Event::End(bytes_) => { + break; + } + _ => return Err(ffail!("Unexpected XML event: {event:?}")), + } } + let Some(resource) = resource else { + return Err(ffail!("No resource was provided")); + }; + let resource = match std::str::from_utf8(resource.as_bytes()) { + Ok(e) => e.to_string(), + Err(err) => return Err(err.into()), + }; + Ok(BindRequest(Resource(resource.into()))) } } }