forked from lavina/lavina
1
0
Fork 0
lavina/crates/proto-xmpp/src/xml/mod.rs

113 lines
3.2 KiB
Rust
Raw Normal View History

use std::ops::Generator;
use std::pin::Pin;
2023-03-07 13:56:31 +00:00
use quick_xml::events::Event;
2023-03-11 15:07:02 +00:00
use quick_xml::name::ResolveResult;
2023-03-07 13:56:31 +00:00
use anyhow::{anyhow, Result};
2023-03-08 18:56:53 +00:00
mod ignore;
pub use ignore::Ignore;
2023-03-08 18:56:53 +00:00
pub trait FromXml: Sized {
type P: Parser<Output = Result<Self>>;
fn parse() -> Self::P;
}
2023-03-12 12:25:23 +00:00
pub trait ToXml: Sized {
fn serialize(&self, events: &mut Vec<Event<'static>>);
}
2023-03-12 13:15:13 +00:00
pub trait FromXmlTag: FromXml {
const NAME: &'static str;
const NS: &'static str;
}
2023-03-07 13:56:31 +00:00
pub trait Parser: Sized {
type Output;
2023-03-11 15:07:02 +00:00
fn consume<'a>(
self: Self,
namespace: ResolveResult,
event: &Event<'a>,
) -> Continuation<Self, Self::Output>;
2023-03-07 13:56:31 +00:00
}
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),
}
}
}
2023-03-07 13:56:31 +00:00
pub enum Continuation<Parser, Res> {
Final(Res),
Continue(Parser),
}
macro_rules! fail_fast {
($errorable: expr) => {
match $errorable {
Ok(i) => i,
Err(e) => return Continuation::Final(Err(e.into())),
2023-03-07 13:56:31 +00:00
}
};
}
#[macro_export]
2023-03-12 21:50:28 +00:00
macro_rules! delegate_parsing {
($parser: ty, $namespace: expr, $event: expr) => {{
let mut parser = <$parser as FromXml>::parse().consume($namespace, $event);
loop {
match parser {
Continuation::Final(Ok(res)) => break Ok(res.into()),
Continuation::Final(Err(err)) => break Err(err),
Continuation::Continue(p) => {
let (namespace, event) = yield;
parser = p.consume(namespace, event);
}
2023-03-12 21:50:28 +00:00
}
}
}};
2023-03-12 21:50:28 +00:00
}
#[macro_export]
2023-03-12 21:50:28 +00:00
macro_rules! match_parser {
($name: expr, $ns: expr, $event: expr; $subtype: ty, $fin: block) => {
2023-03-12 21:50:28 +00:00
if $name.0 == <$subtype as FromXmlTag>::NAME.as_bytes() && $ns == ResolveResult::Bound(Namespace(<$subtype as FromXmlTag>::NS.as_bytes())) {
delegate_parsing!($subtype, $ns, $event)
2023-03-12 21:50:28 +00:00
} else {
$fin
2023-03-12 21:50:28 +00:00
}
};
($name: expr, $ns: expr, $event: expr; $subtype: ty, $($rest: ty),+, $fin: block) => {
2023-03-12 21:50:28 +00:00
if $name.0 == <$subtype as FromXmlTag>::NAME.as_bytes() && $ns == ResolveResult::Bound(Namespace(<$subtype as FromXmlTag>::NS.as_bytes())) {
delegate_parsing!($subtype, $ns, $event)
2023-03-12 21:50:28 +00:00
} else {
match_parser!($name, $ns, $event; $($rest),*, $fin)
2023-03-12 21:50:28 +00:00
}
};
}
pub use delegate_parsing;
pub(crate) use fail_fast;
pub use match_parser;