summaryrefslogtreecommitdiffstats
path: root/src/stanza/iq.rs
diff options
context:
space:
mode:
authorLibravatar cel 🌸 <cel@blos.sm>2023-08-02 00:56:38 +0100
committerLibravatar cel 🌸 <cel@blos.sm>2023-08-02 00:56:38 +0100
commitcd7bb95c0a31d187bfe25bad15043f0b33b111cf (patch)
treec5be0c651198abf736f8867a36906f9345f3a0ac /src/stanza/iq.rs
parent322b2a3b46348ec1c5acbc538de93310c9030b96 (diff)
downloadluz-cd7bb95c0a31d187bfe25bad15043f0b33b111cf.tar.gz
luz-cd7bb95c0a31d187bfe25bad15043f0b33b111cf.tar.bz2
luz-cd7bb95c0a31d187bfe25bad15043f0b33b111cf.zip
implement resource binding
Diffstat (limited to 'src/stanza/iq.rs')
-rw-r--r--src/stanza/iq.rs171
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)
+ }
+}