forked from lavina/lavina
feat(xmpp): iq parsing
This commit is contained in:
parent
27bbabbbbd
commit
d444fc407b
|
@ -23,3 +23,8 @@ Make sure `xmpp.key` starts and ends with:
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
-----END RSA PRIVATE KEY-----
|
-----END RSA PRIVATE KEY-----
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Protocol Specs
|
||||||
|
|
||||||
|
XMPP XSDs - [https://xmpp.org/schemas/index.shtml]
|
||||||
|
|
|
@ -152,6 +152,119 @@ impl MessageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Iq<T> {
|
||||||
|
pub from: Option<String>,
|
||||||
|
pub id: String,
|
||||||
|
pub to: Option<String>,
|
||||||
|
pub r#type: IqType,
|
||||||
|
pub body: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: FromXml> FromXml for Iq<T> {
|
||||||
|
type P = IqParser<T>;
|
||||||
|
|
||||||
|
fn parse() -> Self::P {
|
||||||
|
IqParser(IqParserInner::Init)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IqParser<T: FromXml>(IqParserInner<T>);
|
||||||
|
|
||||||
|
enum IqParserInner<T: FromXml> {
|
||||||
|
Init,
|
||||||
|
ParsingBody(IqParserState<T>, T::P),
|
||||||
|
Final(IqParserState<T>),
|
||||||
|
}
|
||||||
|
struct IqParserState<T> {
|
||||||
|
pub from: Option<String>,
|
||||||
|
pub id: Option<String>,
|
||||||
|
pub to: Option<String>,
|
||||||
|
pub r#type: Option<IqType>,
|
||||||
|
pub body: Option<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: FromXml> Parser for IqParser<T> {
|
||||||
|
type Output = Result<Iq<T>>;
|
||||||
|
|
||||||
|
fn consume<'a>(self: Self, event: &Event<'a>) -> Continuation<Self, Self::Output> {
|
||||||
|
match self.0 {
|
||||||
|
IqParserInner::Init => {
|
||||||
|
if let Event::Start(ref bytes) = event {
|
||||||
|
let mut state: IqParserState<T> = IqParserState { from: None, id: None, to: None, r#type: None, body: None };
|
||||||
|
for attr in bytes.attributes() {
|
||||||
|
let attr = fail_fast!(attr);
|
||||||
|
if attr.key.0 == b"from" {
|
||||||
|
let value = fail_fast!(std::str::from_utf8(&*attr.value));
|
||||||
|
state.from = Some(value.to_string())
|
||||||
|
} else if attr.key.0 == b"id" {
|
||||||
|
let value = fail_fast!(std::str::from_utf8(&*attr.value));
|
||||||
|
state.id = Some(value.to_string())
|
||||||
|
} else if attr.key.0 == b"to" {
|
||||||
|
let value = fail_fast!(std::str::from_utf8(&*attr.value));
|
||||||
|
state.to = Some(value.to_string())
|
||||||
|
} else if attr.key.0 == b"type" {
|
||||||
|
let value = fail_fast!(IqType::from_str(&*attr.value));
|
||||||
|
state.r#type = Some(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Continuation::Continue(IqParser(IqParserInner::ParsingBody(state, T::parse())))
|
||||||
|
} else {
|
||||||
|
Continuation::Final(Err(ffail!("Expected start")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IqParserInner::ParsingBody(mut state, parser) => {
|
||||||
|
match parser.consume(event) {
|
||||||
|
Continuation::Final(f) => {
|
||||||
|
let body = fail_fast!(f);
|
||||||
|
state.body = Some(body);
|
||||||
|
Continuation::Continue(IqParser(IqParserInner::Final(state)))
|
||||||
|
}
|
||||||
|
Continuation::Continue(parser) => {
|
||||||
|
Continuation::Continue(IqParser(IqParserInner::ParsingBody(state, parser)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IqParserInner::Final(state) => {
|
||||||
|
if let Event::End(ref bytes) = event {
|
||||||
|
let id = fail_fast!(state.id.ok_or_else(|| fail("No id provided")));
|
||||||
|
let r#type = fail_fast!(state.r#type.ok_or_else(|| fail("No type provided")));
|
||||||
|
let body = fail_fast!(state.body.ok_or_else(|| fail("No body provided")));
|
||||||
|
Continuation::Final(Ok(Iq {
|
||||||
|
from: state.from,
|
||||||
|
id,
|
||||||
|
to: state.to,
|
||||||
|
r#type,
|
||||||
|
body,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Continuation::Final(Err(ffail!("Unexpected event: {event:?}")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum IqType {
|
||||||
|
Error,
|
||||||
|
Get,
|
||||||
|
Result,
|
||||||
|
Set,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IqType {
|
||||||
|
pub fn from_str(s: &[u8]) -> Result<IqType> {
|
||||||
|
use IqType::*;
|
||||||
|
let s = std::str::from_utf8(s)?;
|
||||||
|
match s {
|
||||||
|
"error" => Ok(Error),
|
||||||
|
"get" => Ok(Get),
|
||||||
|
"result" => Ok(Result),
|
||||||
|
"set" => Ok(Set),
|
||||||
|
t => Err(ffail!("Unknown iq type: {t}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
use quick_xml::events::Event;
|
use quick_xml::events::Event;
|
||||||
|
|
||||||
|
use crate::prelude::Result;
|
||||||
|
|
||||||
|
pub trait FromXml: Sized {
|
||||||
|
type P: Parser<Output = Result<Self>>;
|
||||||
|
|
||||||
|
fn parse() -> Self::P;
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Parser: Sized {
|
pub trait Parser: Sized {
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue