#![allow(unused_variables)] use anyhow::{anyhow, Result}; use quick_xml::events::attributes::Attribute; use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event}; use quick_xml::name::{QName, ResolveResult}; use crate::bind::Jid; use crate::xml::*; pub const XMLNS: &'static str = "http://jabber.org/protocol/muc"; pub const XMLNS_USER: &'static str = "http://jabber.org/protocol/muc#user"; #[derive(PartialEq, Eq, Debug, Default)] pub struct History { pub maxchars: Option, pub maxstanzas: Option, pub seconds: Option, } impl FromXml for History { type P = impl Parser>; fn parse() -> Self::P { |(mut namespace, mut event): (ResolveResult<'static>, &'static Event<'static>)| -> Result { let mut history = History::default(); let (bytes, end) = match event { Event::Start(bytes) if bytes.name().0 == Self::NAME.as_bytes() => (bytes, false), Event::Empty(bytes) if bytes.name().0 == Self::NAME.as_bytes() => (bytes, true), _ => return Err(anyhow!("Unexpected XML event: {event:?}")), }; for attr in bytes.attributes() { let attr = attr?; match attr.key.0 { b"maxchars" => { let s = std::str::from_utf8(&attr.value)?; let a = s.parse()?; history.maxchars = Some(a) } b"maxstanzas" => { let s = std::str::from_utf8(&attr.value)?; let a = s.parse()?; history.maxstanzas = Some(a) } b"seconds" => { let s = std::str::from_utf8(&attr.value)?; let a = s.parse()?; history.seconds = Some(a) } _ => {} } } if end { return Ok(history); } (namespace, event) = yield; let Event::End(bytes) = event else { return Err(anyhow!("Unexpected XML event: {event:?}")); }; Ok(history) } } } impl FromXmlTag for History { const NAME: &'static str = "history"; const NS: &'static str = XMLNS; } #[derive(PartialEq, Eq, Debug)] pub struct Password(pub String); impl FromXml for Password { type P = impl Parser>; fn parse() -> Self::P { |(mut namespace, mut event): (ResolveResult<'static>, &'static Event<'static>)| -> Result { let bytes = match event { Event::Start(bytes) if bytes.name().0 == Self::NAME.as_bytes() => bytes, _ => return Err(anyhow!("Unexpected XML event: {event:?}")), }; (namespace, event) = yield; let Event::Text(bytes) = event else { return Err(anyhow!("Unexpected XML event: {event:?}")); }; let s = std::str::from_utf8(bytes)?.to_string(); (namespace, event) = yield; let Event::End(bytes) = event else { return Err(anyhow!("Unexpected XML event: {event:?}")); }; Ok(Password(s)) } } } impl FromXmlTag for Password { const NAME: &'static str = "password"; const NS: &'static str = XMLNS; } #[derive(PartialEq, Eq, Debug, Default)] pub struct X { pub history: Option, pub password: Option, } impl FromXml for X { type P = impl Parser>; fn parse() -> Self::P { |(mut namespace, mut event): (ResolveResult<'static>, &'static Event<'static>)| -> Result { let mut res = X::default(); let (_, end) = match event { Event::Start(bytes) => (bytes, false), Event::Empty(bytes) => (bytes, true), _ => return Err(anyhow!("Unexpected XML event: {event:?}")), }; if end { return Ok(res); } loop { (namespace, event) = yield; let bytes = match event { Event::Start(bytes) => bytes, Event::Empty(bytes) => bytes, Event::End(_) => break, _ => return Err(anyhow!("Unexpected XML event: {event:?}")), }; if bytes.name().0 == Password::NAME.as_bytes() { let password = delegate_parsing!(Password, namespace, event)?; res.password = Some(password); } else if bytes.name().0 == History::NAME.as_bytes() { let history = delegate_parsing!(History, namespace, event)?; res.history = Some(history); } else { return Err(anyhow!("Unexpected XML event: {event:?}")); } } Ok(res) } } } #[derive(Debug, PartialEq, Eq)] pub struct XUser; impl ToXml for XUser { fn serialize(&self, output: &mut Vec>) { let mut tag = BytesStart::new("x"); tag.push_attribute(("xmlns", XMLNS_USER)); output.push(Event::Empty(tag)); } } #[derive(Debug, PartialEq, Eq)] pub struct Delay { xmlns: String, from: Jid, pub stamp: String, } impl Delay { pub fn new(from: Jid, stamp: String) -> Self { Self { xmlns: "urn:xmpp:delay".into(), from, stamp, } } } #[derive(Debug, PartialEq, Eq)] pub struct XmppHistoryMessage { pub id: String, pub to: Jid, pub from: Jid, pub delay: Delay, pub body: String, } impl From for XmppHistoryMessage { fn from(msg: HistoryMessage) -> Self { Self { id: msg.id, to: msg.to, from: msg.from, delay: Delay::new(msg.from, msg.stamp), body: msg.body, } } } impl ToXml for XmppHistoryMessage { fn serialize(&self, events: &mut Vec>) { let mut tag = BytesStart::new("message"); tag.push_attribute(Attribute { key: QName(b"id"), value: self.id.as_str().as_bytes().into(), }); tag.push_attribute(Attribute { key: QName(b"to"), value: self.to.to_string().into_bytes().into(), }); tag.push_attribute(Attribute { key: QName(b"from"), value: self.from.to_string().into_bytes().into(), }); tag.push_attribute(Attribute { key: QName(b"type"), value: b"groupchat".into(), }); events.push(Event::Start(tag)); let mut tag = BytesStart::new("delay"); tag.push_attribute(Attribute { key: QName(b"xmlns"), value: self.delay.xmlns.as_bytes().into(), }); tag.push_attribute(Attribute { key: QName(b"from"), value: self.delay.from.to_string().into_bytes().into(), }); tag.push_attribute(Attribute { key: QName(b"stamp"), value: self.delay.stamp.as_bytes().into(), }); events.push(Event::Empty(tag)); let mut tag = BytesStart::new("body"); events.push(Event::Start(tag)); events.push(Event::Text(BytesText::new(self.body.to_string().as_str()).into_owned())); events.push(Event::End(BytesEnd::new("body"))); events.push(Event::End(BytesEnd::new("message"))); } } #[cfg(test)] mod test { use super::*; #[test] fn test_history_success_empty() { let input = ""; let res: History = parse(input).unwrap(); let expected = History { maxchars: None, maxstanzas: None, seconds: None, }; assert_eq!(res, expected); } #[test] fn test_history_success_empty_attrs() { let input = r#""#; let res: History = parse(input).unwrap(); let expected = History { maxchars: Some(1), maxstanzas: Some(2), seconds: Some(4), }; assert_eq!(res, expected); } #[test] fn test_history_success_start_end() { let input = r#""#; let res: History = parse(input).unwrap(); let expected = History { maxchars: None, maxstanzas: None, seconds: None, }; assert_eq!(res, expected); } #[test] fn test_history_incorrect_empty() { let input = r#""#; parse::(input).err().unwrap(); } #[test] fn test_password_success() { let input = "olala"; let res: Password = parse(input).unwrap(); let expected = Password("olala".into()); assert_eq!(res, expected); } #[test] fn test_password_incorrect() { let input = r#"asdsd"#; parse::(input).err().unwrap(); } #[test] fn test_x_success_empty() { let input = ""; let res: X = parse(input).unwrap(); let expected = X { history: None, password: None, }; assert_eq!(res, expected); } #[test] fn test_x_success_full() { let input = r#"ololo"#; let res: X = parse(input).unwrap(); let expected = X { history: Some(History { maxchars: Some(1), maxstanzas: None, seconds: None, }), password: Some(Password("ololo".into())), }; assert_eq!(res, expected); } }