1use xso::{AsXml, FromXml};
8
9use crate::data_forms::DataForm;
10use crate::date::DateTime;
11use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
12use crate::ns;
13
14generate_attribute!(
15 Action, "action", {
18 Add => "add",
20
21 Remove => "remove",
23
24 Modify => "modify",
26 }, Default = Add
27);
28
29generate_attribute!(
30 Transport, "transport", {
32 Tcp => "tcp",
34
35 Udp => "udp",
37 }
38);
39
40generate_attribute!(
41 Type, "type", {
43 Stun => "stun",
45
46 Turn => "turn",
48 }
49);
50
51#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
53#[xml(namespace = ns::EXT_DISCO, name = "service")]
54pub struct Service {
55 #[xml(attribute(default))]
58 action: Action,
59
60 #[xml(attribute(default))]
62 expires: Option<DateTime>,
63
64 #[xml(attribute)]
66 host: String,
67
68 #[xml(attribute(default))]
70 name: Option<String>,
71
72 #[xml(attribute(default))]
74 password: Option<String>,
75
76 #[xml(attribute(default))]
78 port: Option<u16>,
79
80 #[xml(attribute(default))]
83 restricted: bool,
84
85 #[xml(attribute(default))]
88 transport: Option<Transport>,
89
90 #[xml(attribute = "type")]
92 type_: Type,
93
94 #[xml(attribute(default))]
96 username: Option<String>,
97
98 #[xml(child(n = ..))]
100 ext_info: Vec<DataForm>,
101}
102
103impl IqGetPayload for Service {}
104
105#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
107#[xml(namespace = ns::EXT_DISCO, name = "services")]
108pub struct ServicesQuery {
109 #[xml(attribute(default, name = "type"))]
111 pub type_: Option<Type>,
112}
113
114impl IqGetPayload for ServicesQuery {}
115
116#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
118#[xml(namespace = ns::EXT_DISCO, name = "services")]
119pub struct ServicesResult {
120 #[xml(attribute(name = "type", default))]
122 pub type_: Option<Type>,
123
124 #[xml(child(n = ..))]
126 pub services: Vec<Service>,
127}
128
129impl IqResultPayload for ServicesResult {}
130impl IqSetPayload for ServicesResult {}
131
132#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
134#[xml(namespace = ns::EXT_DISCO, name = "credentials")]
135pub struct Credentials {
136 #[xml(child(n = ..))]
138 pub services: Vec<Service>,
139}
140
141impl IqGetPayload for Credentials {}
142impl IqResultPayload for Credentials {}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use crate::ns;
148 use minidom::Element;
149
150 #[cfg(target_pointer_width = "32")]
151 #[test]
152 fn test_size() {
153 assert_size!(Action, 1);
154 assert_size!(Transport, 1);
155 assert_size!(Type, 1);
156 assert_size!(Service, 84);
157 assert_size!(ServicesQuery, 1);
158 assert_size!(ServicesResult, 16);
159 assert_size!(Credentials, 12);
160 }
161
162 #[cfg(target_pointer_width = "64")]
163 #[test]
164 fn test_size() {
165 assert_size!(Action, 1);
166 assert_size!(Transport, 1);
167 assert_size!(Type, 1);
168 assert_size!(Service, 144);
169 assert_size!(ServicesQuery, 1);
170 assert_size!(ServicesResult, 32);
171 assert_size!(Credentials, 24);
172 }
173
174 #[test]
175 fn test_simple() {
176 let elem: Element = "<service xmlns='urn:xmpp:extdisco:2' host='stun.shakespeare.lit' port='9998' transport='udp' type='stun'/>".parse().unwrap();
177 let service = Service::try_from(elem).unwrap();
178 assert_eq!(service.action, Action::Add);
179 assert!(service.expires.is_none());
180 assert_eq!(service.host, "stun.shakespeare.lit");
181 assert!(service.name.is_none());
182 assert!(service.password.is_none());
183 assert_eq!(service.port.unwrap(), 9998);
184 assert_eq!(service.restricted, false);
185 assert_eq!(service.transport.unwrap(), Transport::Udp);
186 assert_eq!(service.type_, Type::Stun);
187 assert!(service.username.is_none());
188 assert!(service.ext_info.is_empty());
189 }
190
191 #[test]
192 fn test_service_query() {
193 let query = ServicesQuery { type_: None };
194 let elem = Element::from(query);
195 assert!(elem.is("services", ns::EXT_DISCO));
196 assert_eq!(elem.attrs().next(), None);
197 assert_eq!(elem.nodes().next(), None);
198 }
199
200 #[test]
201 fn test_service_result() {
202 let elem: Element = "<services xmlns='urn:xmpp:extdisco:2' type='stun'><service host='stun.shakespeare.lit' port='9998' transport='udp' type='stun'/></services>".parse().unwrap();
203 let services = ServicesResult::try_from(elem).unwrap();
204 assert_eq!(services.type_, Some(Type::Stun));
205 assert_eq!(services.services.len(), 1);
206 }
207}