aboutsummaryrefslogtreecommitdiffstats
path: root/stanza/src/client/iq.rs
blob: 6ee80ea76082b6030c4908a8f22cdd76878833ec (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::str::FromStr;

use jid::JID;
use peanuts::{
    element::{FromElement, IntoElement},
    DeserializeError, Element, XML_NS,
};

use crate::{
    bind::{self, Bind},
    client::error::Error,
    xep_0199::{self, Ping},
};

use super::XMLNS;

#[derive(Debug)]
pub struct Iq {
    pub from: Option<JID>,
    pub id: String,
    pub to: Option<JID>,
    pub r#type: IqType,
    pub lang: Option<String>,
    // children
    // ##other
    pub query: Option<Query>,
    pub errors: Vec<Error>,
}

#[derive(Clone, Debug)]
pub enum Query {
    Bind(Bind),
    Ping(Ping),
    Unsupported,
}

impl FromElement for Query {
    fn from_element(element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
        match element.identify() {
            (Some(bind::XMLNS), "bind") => Ok(Query::Bind(Bind::from_element(element)?)),
            (Some(xep_0199::XMLNS), "ping") => Ok(Query::Ping(Ping::from_element(element)?)),
            _ => Ok(Query::Unsupported),
        }
    }
}

impl IntoElement for Query {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        match self {
            Query::Bind(bind) => bind.builder(),
            Query::Ping(ping) => ping.builder(),
            // TODO: consider what to do if attempt to serialize unsupported
            Query::Unsupported => todo!(),
        }
    }
}

impl FromElement for Iq {
    fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
        element.check_name("iq")?;
        element.check_namespace(XMLNS)?;

        let from = element.attribute_opt("from")?;
        let id = element.attribute("id")?;
        let to = element.attribute_opt("to")?;
        let r#type = element.attribute("type")?;
        let lang = element.attribute_opt_namespaced("lang", XML_NS)?;
        let query = element.pop_child_opt()?;
        let errors = element.pop_children()?;

        Ok(Iq {
            from,
            id,
            to,
            r#type,
            lang,
            query,
            errors,
        })
    }
}

impl IntoElement for Iq {
    fn builder(&self) -> peanuts::element::ElementBuilder {
        Element::builder("iq", Some(XMLNS))
            .push_attribute_opt("from", self.from.clone())
            .push_attribute("id", self.id.clone())
            .push_attribute_opt("to", self.to.clone())
            .push_attribute("type", self.r#type)
            .push_attribute_opt_namespaced(XML_NS, "lang", self.lang.clone())
            .push_child_opt(self.query.clone())
            .push_children(self.errors.clone())
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum IqType {
    Error,
    Get,
    Result,
    Set,
}

impl FromStr for IqType {
    type Err = DeserializeError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "error" => Ok(IqType::Error),
            "get" => Ok(IqType::Get),
            "result" => Ok(IqType::Result),
            "set" => Ok(IqType::Set),
            _ => Err(DeserializeError::FromStr(s.to_string())),
        }
    }
}

impl ToString for IqType {
    fn to_string(&self) -> String {
        match self {
            IqType::Error => "error".to_string(),
            IqType::Get => "get".to_string(),
            IqType::Result => "result".to_string(),
            IqType::Set => "set".to_string(),
        }
    }
}