use nanoid::nanoid; use quick_xml::{ events::{BytesStart, Event}, name::QName, Reader, Writer, }; use crate::{JabberClient, JabberError, JID}; use crate::Result; #[derive(Debug)] pub struct IQ { to: Option, from: Option, id: String, r#type: IQType, lang: Option, 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, from: Option, element: R, ) -> Result> { 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>> { Some(vec![self.child.clone()]) } } impl TryFrom> for IQ { type Error = JabberError; fn try_from(element: Element<'static>) -> std::result::Result { if let Event::Start(start) = &element.event { if start.name() == QName(b"iq") { let mut to: Option = None; let mut from: Option = 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 = 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) } }