From 1996c3d6c790221547ca593e30c29bcd031c478a Mon Sep 17 00:00:00 2001 From: Mikhail Date: Tue, 30 Apr 2024 14:16:52 +0200 Subject: [PATCH] Add parsing and test --- crates/proto-xmpp/src/lib.rs | 2 + crates/proto-xmpp/src/mam.rs | 167 ++++++++++++++++++++++++++++++----- 2 files changed, 148 insertions(+), 21 deletions(-) diff --git a/crates/proto-xmpp/src/lib.rs b/crates/proto-xmpp/src/lib.rs index 7283c50..09c08d2 100644 --- a/crates/proto-xmpp/src/lib.rs +++ b/crates/proto-xmpp/src/lib.rs @@ -1,5 +1,7 @@ #![feature(coroutines, coroutine_trait, type_alias_impl_trait, impl_trait_in_assoc_type)] +extern crate core; + pub mod bind; pub mod client; pub mod disco; diff --git a/crates/proto-xmpp/src/mam.rs b/crates/proto-xmpp/src/mam.rs index 796a3d6..464844c 100644 --- a/crates/proto-xmpp/src/mam.rs +++ b/crates/proto-xmpp/src/mam.rs @@ -1,26 +1,110 @@ use anyhow::{anyhow, Result}; use quick_xml::events::Event; use quick_xml::name::{Namespace, ResolveResult}; +use std::io::Read; use crate::xml::*; -pub const XMLNS: &'static str = "urn:xmpp:mam:2"; +pub const MAM_XMLNS: &'static str = "urn:xmpp:mam:2"; +pub const DATA_XMLNS: &'static str = "jabber:x:data"; #[derive(PartialEq, Eq, Debug, Clone)] -pub struct MessageArchiveRequest; +pub struct MessageArchiveRequest { + x: Option, +} + +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct X { + fields: Vec, +} + +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Field { + values: Vec, +} + +impl FromXmlTag for X { + const NAME: &'static str = "x"; + const NS: &'static str = DATA_XMLNS; +} impl FromXmlTag for MessageArchiveRequest { const NAME: &'static str = "query"; - const NS: &'static str = XMLNS; + const NS: &'static str = MAM_XMLNS; +} + +impl FromXml for X { + type P = impl Parser>; + + fn parse() -> Self::P { + |(mut namespace, mut event): (ResolveResult<'static>, &'static Event<'static>)| -> Result { + println!("X::parse {:?}", event); + + let bytes = match event { + Event::Start(bytes) if bytes.name().0 == X::NAME.as_bytes() => bytes, + Event::Empty(bytes) if bytes.name().0 == X::NAME.as_bytes() => return Ok(X { fields: vec![] }), + _ => return Err(anyhow!("Unexpected XML event: {event:?}")), + }; + let mut fields = vec![]; + loop { + (namespace, event) = yield; + match event { + Event::Start(bytes) => { + // start of + let mut values = vec![]; + loop { + (namespace, event) = yield; + match event { + Event::Start(bytes) if bytes.name().0 == b"value" => { + // start of + } + Event::End(bytes) if bytes.name().0 == b"field" => { + // end of + break; + } + _ => return Err(anyhow!("Unexpected XML event: {event:?}")), + } + (namespace, event) = yield; + let text: String = match event { + Event::Text(bytes) => { + // text inside + String::from_utf8(bytes.to_vec())? + } + _ => return Err(anyhow!("Unexpected XML event: {event:?}")), + }; + (namespace, event) = yield; + match event { + Event::End(bytes) if bytes.name().0 == b"value" => { + // end of + } + _ => return Err(anyhow!("Unexpected XML event: {event:?}")), + } + values.push(text); + } + fields.push(Field { values }) + } + Event::End(bytes) if bytes.name().0 == X::NAME.as_bytes() => { + // end of + return Ok(X { fields }); + } + _ => return Err(anyhow!("Unexpected XML event: {event:?}")), + } + } + } + } } impl FromXml for MessageArchiveRequest { type P = impl Parser>; fn parse() -> Self::P { - |(namespace, event): (ResolveResult<'static>, &'static Event<'static>)| -> Result { - let Event::Start(bytes) = event else { - return Err(anyhow!("Unexpected XML event: {event:?}")); + |(mut namespace, mut event): (ResolveResult<'static>, &'static Event<'static>)| -> Result { + println!("MessageArchiveRequest::parse {:?}", event); + + let bytes = match event { + Event::Empty(_) => return Ok(MessageArchiveRequest { x: None }), + Event::Start(bytes) => bytes, + _ => return Err(anyhow!("Unexpected XML event: {event:?}")), }; if bytes.name().0 != MessageArchiveRequest::NAME.as_bytes() { return Err(anyhow!("Unexpected XML tag: {:?}", bytes.name())); @@ -28,19 +112,20 @@ impl FromXml for MessageArchiveRequest { let ResolveResult::Bound(Namespace(ns)) = namespace else { return Err(anyhow!("No namespace provided")); }; - if ns != XMLNS.as_bytes() { + if ns != MAM_XMLNS.as_bytes() { return Err(anyhow!("Incorrect namespace")); } - loop { - let (namespace, event) = yield; - match event { - Event::End(bytes) if bytes.name().0 == MessageArchiveRequest::NAME.as_bytes() => { - break; - } - _ => return Err(anyhow!("Unexpected XML event: {event:?}")), + (namespace, event) = yield; + match event { + Event::End(bytes) if bytes.name().0 == MessageArchiveRequest::NAME.as_bytes() => { + Ok(MessageArchiveRequest { x: None }) } + Event::Start(bytes) | Event::Empty(bytes) if bytes.name().0 == X::NAME.as_bytes() => { + let x = delegate_parsing!(X, namespace, event)?; + Ok(MessageArchiveRequest { x: Some(x) }) + } + _ => Err(anyhow!("Unexpected XML event: {event:?}")), } - Ok(MessageArchiveRequest) } } } @@ -50,22 +135,62 @@ impl MessageArchiveRequest {} #[cfg(test)] mod tests { use super::*; + use crate::bind::{Jid, Name, Server}; use crate::client::{Iq, IqType}; #[test] - fn test_parse() { - let input = r#""#; + fn test_parse_archive_query() { + let input = r#""#; let result: Iq = parse(input).unwrap(); assert_eq!( result, Iq { from: None, - id: "mam_1".to_string(), - to: None, - r#type: IqType::Get, - body: MessageArchiveRequest, + id: "juliet1".to_string(), + to: Option::from(Jid { + name: None, + server: Server("pubsub.shakespeare.lit".into()), + resource: None, + }), + r#type: IqType::Set, + body: MessageArchiveRequest { x: None }, } ); } + + #[test] + fn test_parse_query_messages_from_jid() { + let input = r#"value1juliet@capulet.lit"#; + + let result: Iq = parse(input).unwrap(); + assert_eq!( + result, + Iq { + from: None, + id: "juliet1".to_string(), + to: None, + r#type: IqType::Set, + body: MessageArchiveRequest { + x: Some(X { + fields: vec![ + Field { + values: vec!["value1".to_string()], + }, + Field { + values: vec!["juliet@capulet.lit".to_string()], + }, + ] + }) + }, + } + ); + } + + #[test] + fn test_parse_query_messages_from_jid_with_unclosed_tag() { + let input = r#"value1juliet@capulet.lit"#; + + assert!(parse::>(input).is_err()) + } }