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)] #[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<Box<[u8]>> = 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(ffail!("Unexpected XML event: {event:?}"))); return Err(ffail!("Unexpected XML event: {event:?}"));
}; };
if bytes.name().0 != BindRequest::NAME.as_bytes() { if bytes.name().0 != BindRequest::NAME.as_bytes() {
return Continuation::Final(Err(ffail!( return Err(ffail!("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(ffail!("No namespace provided"))); return Err(ffail!("No namespace provided"));
}; };
if ns != XMLNS.as_bytes() { if ns != XMLNS.as_bytes() {
return Continuation::Final(Err(ffail!("Incorrect namespace"))); return Err(ffail!("Incorrect namespace"));
} }
Continuation::Continue(BindRequestParser(InBind(None))) 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:?}")),
} }
InBind(resource) => match event {
Event::Start(bytes) => {
Continuation::Continue(BindRequestParser(InBindResourceInitial))
} }
Event::End(bytes) => {
let Some(resource) = resource else { let Some(resource) = resource else {
return Continuation::Final(Err(ffail!("No resource was provided"))); return Err(ffail!("No resource was provided"));
}; };
Continuation::Final(Ok(BindRequest(Resource(resource.into())))) let resource = match std::str::from_utf8(resource.as_bytes()) {
}
_ => 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(), Ok(e) => e.to_string(),
Err(err) => return Continuation::Final(Err(err.into())), Err(err) => return Err(err.into()),
}; };
Continuation::Continue(BindRequestParser(InBindResourceEnd(resource))) Ok(BindRequest(Resource(resource.into())))
}
InBindResourceEnd(resource) => {
let Event::End(bytes) = event else {
return Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}")));
};
Continuation::Continue(BindRequestParser(InBind(Some(resource))))
}
} }
} }
} }