diff options
Diffstat (limited to '')
-rw-r--r-- | src/stanza/iq.rs | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/src/stanza/iq.rs b/src/stanza/iq.rs new file mode 100644 index 0000000..8a373b2 --- /dev/null +++ b/src/stanza/iq.rs @@ -0,0 +1,171 @@ +use nanoid::nanoid; +use quick_xml::{ + events::{BytesStart, Event}, + name::QName, + Reader, Writer, +}; + +use crate::{JabberClient, JabberError, JID}; + +use super::{Element, IntoElement}; +use crate::Result; + +#[derive(Debug)] +pub struct IQ { + to: Option<JID>, + from: Option<JID>, + id: String, + r#type: IQType, + lang: Option<String>, + child: Element<'static>, +} + +#[derive(Debug)] +enum IQType { + Get, + Set, + Result, + Error, +} + +impl IQ { + pub async fn set<'j, R: IntoElement<'static>>( + client: &mut JabberClient<'j>, + to: Option<JID>, + from: Option<JID>, + element: R, + ) -> Result<Element<'static>> { + let id = nanoid!(); + let iq = IQ { + to, + from, + id: id.clone(), + r#type: IQType::Set, + lang: None, + child: Element::from(element), + }; + println!("{:?}", iq); + let iq = Element::from(iq); + println!("{:?}", iq); + iq.write(&mut client.writer).await?; + let result = Element::read(&mut client.reader).await?; + let iq = IQ::try_from(result)?; + if iq.id == id { + return Ok(iq.child); + } + Err(JabberError::IDMismatch) + } +} + +impl<'e> IntoElement<'e> for IQ { + fn event(&self) -> quick_xml::events::Event<'e> { + let mut start = BytesStart::new("iq"); + if let Some(to) = &self.to { + start.push_attribute(("to", to.to_string().as_str())); + } + if let Some(from) = &self.from { + start.push_attribute(("from", from.to_string().as_str())); + } + start.push_attribute(("id", self.id.as_str())); + match self.r#type { + IQType::Get => start.push_attribute(("type", "get")), + IQType::Set => start.push_attribute(("type", "set")), + IQType::Result => start.push_attribute(("type", "result")), + IQType::Error => start.push_attribute(("type", "error")), + } + if let Some(lang) = &self.lang { + start.push_attribute(("from", lang.to_string().as_str())); + } + + quick_xml::events::Event::Start(start) + } + + fn children(&self) -> Option<Vec<Element<'e>>> { + Some(vec![self.child.clone()]) + } +} + +impl TryFrom<Element<'static>> for IQ { + type Error = JabberError; + + fn try_from(element: Element<'static>) -> std::result::Result<Self, Self::Error> { + if let Event::Start(start) = &element.event { + if start.name() == QName(b"iq") { + let mut to: Option<JID> = None; + let mut from: Option<JID> = None; + let mut id = None; + let mut r#type = None; + let mut lang = None; + start + .attributes() + .into_iter() + .try_for_each(|attribute| -> Result<()> { + if let Ok(attribute) = attribute { + let buf: Vec<u8> = Vec::new(); + let reader = Reader::from_reader(buf); + match attribute.key { + QName(b"to") => { + to = Some( + attribute + .decode_and_unescape_value(&reader) + .or(Err(JabberError::Utf8Decode))? + .into_owned() + .try_into()?, + ) + } + QName(b"from") => { + from = Some( + attribute + .decode_and_unescape_value(&reader) + .or(Err(JabberError::Utf8Decode))? + .into_owned() + .try_into()?, + ) + } + QName(b"id") => { + id = Some( + attribute + .decode_and_unescape_value(&reader) + .or(Err(JabberError::Utf8Decode))? + .into_owned(), + ) + } + QName(b"type") => { + let value = attribute + .decode_and_unescape_value(&reader) + .or(Err(JabberError::Utf8Decode))?; + match value.as_ref() { + "get" => r#type = Some(IQType::Get), + "set" => r#type = Some(IQType::Set), + "result" => r#type = Some(IQType::Result), + "error" => r#type = Some(IQType::Error), + _ => return Err(JabberError::ParseError), + } + } + QName(b"lang") => { + lang = Some( + attribute + .decode_and_unescape_value(&reader) + .or(Err(JabberError::Utf8Decode))? + .into_owned(), + ) + } + _ => return Err(JabberError::UnknownAttribute), + } + } + Ok(()) + })?; + let iq = IQ { + to, + from, + id: id.ok_or(JabberError::NoID)?, + r#type: r#type.ok_or(JabberError::NoType)?, + lang, + child: element.child()?.to_owned(), + }; + return Ok(iq); + } + } + Err(JabberError::ParseError) + } +} |