forked from lavina/lavina
generator-based parsing of xmpp stanzas
This commit is contained in:
parent
bba1ea107d
commit
d0f807841c
|
@ -0,0 +1 @@
|
||||||
|
nightly
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![feature(generators, generator_trait, type_alias_impl_trait)]
|
||||||
|
|
||||||
mod core;
|
mod core;
|
||||||
mod prelude;
|
mod prelude;
|
||||||
mod projections;
|
mod projections;
|
||||||
|
|
|
@ -18,58 +18,25 @@ pub enum IqClientBody {
|
||||||
Unknown(Ignore),
|
Unknown(Ignore),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(From)]
|
|
||||||
pub struct IqClientBodyParser(IqClientBodyParserInner);
|
|
||||||
|
|
||||||
#[derive(From)]
|
|
||||||
enum IqClientBodyParserInner {
|
|
||||||
Initial,
|
|
||||||
Bind(<BindRequest as FromXml>::P),
|
|
||||||
SessionV(<Session as FromXml>::P),
|
|
||||||
RosterV(<RosterQuery as FromXml>::P),
|
|
||||||
UnknownV(<Ignore as FromXml>::P),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromXml for IqClientBody {
|
impl FromXml for IqClientBody {
|
||||||
type P = IqClientBodyParser;
|
type P = impl Parser<Output = Result<Self>>;
|
||||||
|
|
||||||
fn parse() -> Self::P {
|
fn parse() -> Self::P {
|
||||||
IqClientBodyParserInner::Initial.into()
|
|(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result<Self> {
|
||||||
}
|
let bytes = match event {
|
||||||
}
|
Event::Start(bytes) => bytes,
|
||||||
|
Event::Empty(bytes) => bytes,
|
||||||
impl Parser for IqClientBodyParser {
|
_ => return Err(ffail!("Unexpected XML event: {event:?}")),
|
||||||
type Output = Result<IqClientBody>;
|
};
|
||||||
|
let name = bytes.name();
|
||||||
fn consume<'a>(
|
match_parser!(name, namespace, event;
|
||||||
self: Self,
|
BindRequest,
|
||||||
namespace: quick_xml::name::ResolveResult,
|
Session,
|
||||||
event: &quick_xml::events::Event<'a>,
|
RosterQuery,
|
||||||
) -> crate::util::xml::Continuation<Self, Self::Output> {
|
{
|
||||||
use IqClientBodyParserInner::*;
|
delegate_parsing!(Ignore, namespace, event).into()
|
||||||
match self.0 {
|
}
|
||||||
Initial => {
|
)
|
||||||
let bytes = match event {
|
|
||||||
Event::Start(bytes) => bytes,
|
|
||||||
Event::Empty(bytes) => bytes,
|
|
||||||
_ => {
|
|
||||||
return Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}")))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let name = bytes.name();
|
|
||||||
match_parser!(IqClientBodyParser, name, namespace, event;
|
|
||||||
BindRequest,
|
|
||||||
Session,
|
|
||||||
RosterQuery,
|
|
||||||
{
|
|
||||||
IqClientBodyParser(Ignore::parse().into()).consume(namespace, event)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Bind(p) => delegate_parsing!(p, IqClientBodyParserInner, namespace, event),
|
|
||||||
SessionV(p) => delegate_parsing!(p, IqClientBodyParserInner, namespace, event),
|
|
||||||
RosterV(p) => delegate_parsing!(p, IqClientBodyParserInner, namespace, event),
|
|
||||||
UnknownV(p) => delegate_parsing!(p, IqClientBodyParserInner, namespace, event),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,52 +48,27 @@ pub enum ClientPacket {
|
||||||
Presence(Presence<Ignore>),
|
Presence(Presence<Ignore>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(From)]
|
|
||||||
pub struct ClientPacketParser(ClientPacketParserInner);
|
|
||||||
|
|
||||||
impl FromXml for ClientPacket {
|
impl FromXml for ClientPacket {
|
||||||
type P = ClientPacketParser;
|
type P = impl Parser<Output = Result<Self>>;
|
||||||
|
|
||||||
fn parse() -> Self::P {
|
fn parse() -> Self::P {
|
||||||
ClientPacketParserInner::Initial.into()
|
|(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result<Self> {
|
||||||
}
|
let Event::Start(bytes) = event else {
|
||||||
}
|
return Err(ffail!("Unexpected XML event: {event:?}"));
|
||||||
|
};
|
||||||
#[derive(From)]
|
let name = bytes.name();
|
||||||
enum ClientPacketParserInner {
|
match_parser!(name, namespace, event;
|
||||||
Initial,
|
Iq::<IqClientBody>,
|
||||||
IqV(<Iq<IqClientBody> as FromXml>::P),
|
Presence::<Ignore>,
|
||||||
MessageV(<Message as FromXml>::P),
|
Message,
|
||||||
PresenceV(<Presence<Ignore> as FromXml>::P),
|
{
|
||||||
}
|
Err(ffail!(
|
||||||
|
"Unexpected XML event of name {:?} in namespace {:?}",
|
||||||
impl Parser for ClientPacketParser {
|
name,
|
||||||
type Output = Result<ClientPacket>;
|
namespace
|
||||||
|
))
|
||||||
fn consume<'a>(
|
}
|
||||||
self: Self,
|
)
|
||||||
namespace: ResolveResult,
|
|
||||||
event: &Event<'a>,
|
|
||||||
) -> Continuation<Self, Self::Output> {
|
|
||||||
use ClientPacketParserInner::*;
|
|
||||||
match self.0 {
|
|
||||||
Initial => {
|
|
||||||
let Event::Start(bytes) = event else {
|
|
||||||
return Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}")));
|
|
||||||
};
|
|
||||||
let name = bytes.name();
|
|
||||||
match_parser!(ClientPacketParser, name, namespace, event;
|
|
||||||
Iq::<IqClientBody>,
|
|
||||||
Presence::<Ignore>,
|
|
||||||
Message,
|
|
||||||
{
|
|
||||||
Continuation::Final(Err(ffail!("Unexpected XML event of name {:?} in namespace {:?}", name, namespace)))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
IqV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event),
|
|
||||||
MessageV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event),
|
|
||||||
PresenceV(p) => delegate_parsing!(p, ClientPacketParserInner, namespace, event),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use std::ops::Generator;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
use quick_xml::events::Event;
|
use quick_xml::events::Event;
|
||||||
use quick_xml::name::ResolveResult;
|
use quick_xml::name::ResolveResult;
|
||||||
|
|
||||||
|
@ -31,6 +34,30 @@ pub trait Parser: Sized {
|
||||||
) -> Continuation<Self, Self::Output>;
|
) -> Continuation<Self, Self::Output>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T, Out> Parser for T
|
||||||
|
where
|
||||||
|
T: Generator<(ResolveResult<'static>, &'static Event<'static>), Yield = (), Return = Out>
|
||||||
|
+ Unpin,
|
||||||
|
{
|
||||||
|
type Output = Out;
|
||||||
|
|
||||||
|
fn consume<'a>(
|
||||||
|
mut self: Self,
|
||||||
|
namespace: ResolveResult,
|
||||||
|
event: &Event<'a>,
|
||||||
|
) -> Continuation<Self, Self::Output> {
|
||||||
|
let s = Pin::new(&mut self);
|
||||||
|
// this is a very rude workaround fixing the fact that rust generators
|
||||||
|
// 1. don't support higher-kinded lifetimes (i.e. no `impl for <'a> Generator<Event<'a>>)
|
||||||
|
// 2. don't track borrows across yield points and lack thereof
|
||||||
|
// implementors of Parser should manually check that inputs are not used across yields
|
||||||
|
match s.resume(unsafe { std::mem::transmute((namespace, event)) }) {
|
||||||
|
std::ops::GeneratorState::Yielded(()) => Continuation::Continue(self),
|
||||||
|
std::ops::GeneratorState::Complete(res) => Continuation::Final(res),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum Continuation<Parser, Res> {
|
pub enum Continuation<Parser, Res> {
|
||||||
Final(Res),
|
Final(Res),
|
||||||
Continue(Parser),
|
Continue(Parser),
|
||||||
|
@ -46,31 +73,34 @@ macro_rules! fail_fast {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! delegate_parsing {
|
macro_rules! delegate_parsing {
|
||||||
($parser: ident, $intermediate: ident, $namespace: expr, $event: expr) => {
|
($parser: ty, $namespace: expr, $event: expr) => {{
|
||||||
match $parser.consume($namespace, $event) {
|
let mut parser = <$parser as FromXml>::parse().consume($namespace, $event);
|
||||||
Continuation::Final(Ok(r)) => Continuation::Final(Ok(r.into())),
|
loop {
|
||||||
Continuation::Final(Err(e)) => Continuation::Final(Err(e)),
|
match parser {
|
||||||
Continuation::Continue(s) => {
|
Continuation::Final(Ok(res)) => break Ok(res.into()),
|
||||||
let inner: $intermediate = s.into();
|
Continuation::Final(Err(err)) => break Err(err),
|
||||||
Continuation::Continue(inner.into())
|
Continuation::Continue(p) => {
|
||||||
|
let (namespace, event) = yield;
|
||||||
|
parser = p.consume(namespace, event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! match_parser {
|
macro_rules! match_parser {
|
||||||
($outer: ident, $name: expr, $ns: expr, $event: expr; $subtype: ty, $fin: block) => {
|
($name: expr, $ns: expr, $event: expr; $subtype: ty, $fin: block) => {
|
||||||
if $name.0 == <$subtype as FromXmlTag>::NAME.as_bytes() && $ns == ResolveResult::Bound(Namespace(<$subtype as FromXmlTag>::NS.as_bytes())) {
|
if $name.0 == <$subtype as FromXmlTag>::NAME.as_bytes() && $ns == ResolveResult::Bound(Namespace(<$subtype as FromXmlTag>::NS.as_bytes())) {
|
||||||
$outer(<$subtype as FromXml>::parse().into()).consume($ns, $event)
|
delegate_parsing!($subtype, $ns, $event)
|
||||||
} else {
|
} else {
|
||||||
$fin
|
$fin
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($outer: ident, $name: expr, $ns: expr, $event: expr; $subtype: ty, $($rest: ty),+, $fin: block) => {
|
($name: expr, $ns: expr, $event: expr; $subtype: ty, $($rest: ty),+, $fin: block) => {
|
||||||
if $name.0 == <$subtype as FromXmlTag>::NAME.as_bytes() && $ns == ResolveResult::Bound(Namespace(<$subtype as FromXmlTag>::NS.as_bytes())) {
|
if $name.0 == <$subtype as FromXmlTag>::NAME.as_bytes() && $ns == ResolveResult::Bound(Namespace(<$subtype as FromXmlTag>::NS.as_bytes())) {
|
||||||
$outer(<$subtype as FromXml>::parse().into()).consume($ns, $event)
|
delegate_parsing!($subtype, $ns, $event)
|
||||||
} else {
|
} else {
|
||||||
match_parser!($outer, $name, $ns, $event; $($rest),*, $fin)
|
match_parser!($name, $ns, $event; $($rest),*, $fin)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue