From 1b3551f108f29d97b942be48f8f0703573540d7c Mon Sep 17 00:00:00 2001 From: Nikita Vilunov Date: Sat, 30 Sep 2023 16:02:18 +0000 Subject: [PATCH] xmpp: rewrite bind request parser as a coroutine (#12) Reviewed-on: https://git.vilunov.me/lavina/lavina/pulls/12 --- crates/proto-xmpp/src/bind.rs | 109 ++++++++++++---------------------- 1 file changed, 38 insertions(+), 71 deletions(-) diff --git a/crates/proto-xmpp/src/bind.rs b/crates/proto-xmpp/src/bind.rs index e6ef5d9..8d9a4cf 100644 --- a/crates/proto-xmpp/src/bind.rs +++ b/crates/proto-xmpp/src/bind.rs @@ -78,89 +78,56 @@ 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(anyhow!("Unexpected XML event: {event:?}"))); - }; - if bytes.name().0 != BindRequest::NAME.as_bytes() { - return Continuation::Final(Err(anyhow!( - "Unexpected XML tag: {:?}", - bytes.name() - ))); - } - let ResolveResult::Bound(Namespace(ns)) = namespace else { - return Continuation::Final(Err(anyhow!("No namespace provided"))); - }; - if ns != XMLNS.as_bytes() { - return Continuation::Final(Err(anyhow!("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(anyhow!("Unexpected XML event: {event:?}")); + }; + if bytes.name().0 != BindRequest::NAME.as_bytes() { + return Err(anyhow!("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(anyhow!("No resource was provided"))); + let ResolveResult::Bound(Namespace(ns)) = namespace else { + return Err(anyhow!("No namespace provided")); + }; + if ns != XMLNS.as_bytes() { + return Err(anyhow!("Incorrect namespace")); + } + loop { + let (namespace, event) = yield; + match event { + Event::Start(bytes) if bytes.name().0 == b"resource" => { + let (namespace, event) = yield; + let Event::Text(text) = event else { + return Err(anyhow!("Unexpected XML event: {event:?}")); }; - Continuation::Final(Ok(BindRequest(Resource(resource.into())))) + resource = Some(std::str::from_utf8(&*text)?.into()); + let (namespace, event) = yield; + let Event::End(bytes) = event else { + return Err(anyhow!("Unexpected XML event: {event:?}")); + }; + if bytes.name().0 != b"resource" { + return Err(anyhow!("Unexpected XML tag: {:?}", bytes.name())); + } + } + Event::End(bytes) if bytes.name().0 == BindRequest::NAME.as_bytes() => { + break; + } + _ => return Err(anyhow!("Unexpected XML event: {event:?}")), } - _ => Continuation::Final(Err(anyhow!("Unexpected XML event: {event:?}"))), - }, - InBindResourceInitial => { - let Event::Text(text) = event else { - return Continuation::Final(Err(anyhow!("Unexpected XML event: {event:?}"))); - }; - let resource = match std::str::from_utf8(&*text) { - Ok(e) => e.to_string(), - Err(err) => return Continuation::Final(Err(err.into())), - }; - Continuation::Continue(BindRequestParser(InBindResourceEnd(resource))) - } - InBindResourceEnd(resource) => { - let Event::End(bytes) = event else { - return Continuation::Final(Err(anyhow!("Unexpected XML event: {event:?}"))); - }; - Continuation::Continue(BindRequestParser(InBind(Some(resource)))) } + let Some(resource) = resource else { + return Err(anyhow!("No resource was provided")); + }; + Ok(BindRequest(Resource(resource))) } } }