xmpp: rewrite bind request parser as a coroutine (#12)

Reviewed-on: lavina/lavina#12
This commit is contained in:
Nikita Vilunov 2023-09-30 16:02:18 +00:00
parent 444b608e96
commit 1b3551f108
1 changed files with 38 additions and 71 deletions

View File

@ -78,90 +78,57 @@ impl Jid {
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub struct BindRequest(pub Resource); pub struct BindRequest(pub Resource);
pub struct BindRequestParser(BindRequestParserInner);
enum BindRequestParserInner {
Initial,
/// Consumed <bind> start and expects <resource>
InBind(Option<String>),
/// Consumed <resource> start
InBindResourceInitial,
/// Consumer <resource> start and inner text
InBindResourceEnd(String),
}
impl FromXmlTag for BindRequest { impl FromXmlTag for BindRequest {
const NS: &'static str = XMLNS; const NS: &'static str = XMLNS;
const NAME: &'static str = "bind"; const NAME: &'static str = "bind";
} }
impl FromXml for BindRequest { impl FromXml for BindRequest {
type P = BindRequestParser; type P = impl Parser<Output = Result<Self>>;
fn parse() -> Self::P { fn parse() -> Self::P {
BindRequestParser(BindRequestParserInner::Initial) |(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result<Self> {
} let mut resource: Option<Str> = None;
}
// TODO rewrite as a generator
impl Parser for BindRequestParser {
type Output = Result<BindRequest>;
fn consume<'a>(
self: Self,
namespace: ResolveResult,
event: &Event<'a>,
) -> Continuation<Self, Self::Output> {
// TODO validate tag names and namespaces
use BindRequestParserInner::*;
match self.0 {
Initial => {
let Event::Start(bytes) = event else { let Event::Start(bytes) = event else {
return Continuation::Final(Err(anyhow!("Unexpected XML event: {event:?}"))); return Err(anyhow!("Unexpected XML event: {event:?}"));
}; };
if bytes.name().0 != BindRequest::NAME.as_bytes() { if bytes.name().0 != BindRequest::NAME.as_bytes() {
return Continuation::Final(Err(anyhow!( return Err(anyhow!("Unexpected XML tag: {:?}", bytes.name()));
"Unexpected XML tag: {:?}",
bytes.name()
)));
} }
let ResolveResult::Bound(Namespace(ns)) = namespace else { let ResolveResult::Bound(Namespace(ns)) = namespace else {
return Continuation::Final(Err(anyhow!("No namespace provided"))); return Err(anyhow!("No namespace provided"));
}; };
if ns != XMLNS.as_bytes() { if ns != XMLNS.as_bytes() {
return Continuation::Final(Err(anyhow!("Incorrect namespace"))); return Err(anyhow!("Incorrect namespace"));
} }
Continuation::Continue(BindRequestParser(InBind(None))) loop {
} let (namespace, event) = yield;
InBind(resource) => match event { match event {
Event::Start(bytes) => { Event::Start(bytes) if bytes.name().0 == b"resource" => {
Continuation::Continue(BindRequestParser(InBindResourceInitial)) let (namespace, event) = yield;
}
Event::End(bytes) => {
let Some(resource) = resource else {
return Continuation::Final(Err(anyhow!("No resource was provided")));
};
Continuation::Final(Ok(BindRequest(Resource(resource.into()))))
}
_ => Continuation::Final(Err(anyhow!("Unexpected XML event: {event:?}"))),
},
InBindResourceInitial => {
let Event::Text(text) = event else { let Event::Text(text) = event else {
return Continuation::Final(Err(anyhow!("Unexpected XML event: {event:?}"))); return Err(anyhow!("Unexpected XML event: {event:?}"));
}; };
let resource = match std::str::from_utf8(&*text) { resource = Some(std::str::from_utf8(&*text)?.into());
Ok(e) => e.to_string(), let (namespace, event) = yield;
Err(err) => return Continuation::Final(Err(err.into())),
};
Continuation::Continue(BindRequestParser(InBindResourceEnd(resource)))
}
InBindResourceEnd(resource) => {
let Event::End(bytes) = event else { let Event::End(bytes) = event else {
return Continuation::Final(Err(anyhow!("Unexpected XML event: {event:?}"))); return Err(anyhow!("Unexpected XML event: {event:?}"));
}; };
Continuation::Continue(BindRequestParser(InBind(Some(resource)))) 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:?}")),
}
}
let Some(resource) = resource else {
return Err(anyhow!("No resource was provided"));
};
Ok(BindRequest(Resource(resource)))
}
} }
} }