summaryrefslogblamecommitdiffstats
path: root/src/stanza/iq.rs
blob: 6c7dee3c8b1b6bb28ecff928e2e5ea35cbdf7df7 (plain) (tree)
1
2
3
4
5
6
7
8
9








                                            
































































































































































                                                                                      
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<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)
    }
}