forked from lavina/lavina
1
0
Fork 0

xmpp: rewrite bind request parser as a coroutine

This commit is contained in:
Nikita Vilunov 2023-09-19 22:11:43 +02:00
parent 87d73af811
commit 372efc3701
1 changed files with 40 additions and 72 deletions

View File

@ -78,89 +78,57 @@ impl Jid {
#[derive(PartialEq, Eq, Debug)]
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 {
const NS: &'static str = XMLNS;
const NAME: &'static str = "bind";
}
impl FromXml for BindRequest {
type P = BindRequestParser;
type P = impl Parser<Output = Result<Self>>;
fn parse() -> Self::P {
BindRequestParser(BindRequestParserInner::Initial)
}
}
// 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 {
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<Self> {
let mut resource: Option<Box<[u8]>> = 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())))
}
}
}