1// Copyright (c) 2019 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6use crate::ns;
7use crate::util::error::Error;
8use crate::Element;
9use std::convert::TryFrom;
10
11generate_attribute!(
12 /// Whether a conference bookmark should be joined automatically.
13 Autojoin,
14 "autojoin",
15 bool
16);
17
18/// A conference bookmark.
19#[derive(Debug, Clone, Default)]
20pub struct Conference {
21 /// Whether a conference bookmark should be joined automatically.
22 pub autojoin: Autojoin,
23
24 /// A user-defined name for this conference.
25 pub name: Option<String>,
26
27 /// The nick the user will use to join this conference.
28 pub nick: Option<String>,
29
30 /// The password required to join this conference.
31 pub password: Option<String>,
32
33 /// Extensions elements.
34 pub extensions: Option<Vec<Element>>,
35}
36
37impl Conference {
38 /// Create a new conference.
39 pub fn new() -> Conference {
40 Conference::default()
41 }
42}
43
44impl TryFrom<Element> for Conference {
45 type Error = Error;
46
47 fn try_from(root: Element) -> Result<Conference, Error> {
48 check_self!(root, "conference", BOOKMARKS2, "Conference");
49 check_no_unknown_attributes!(root, "Conference", ["autojoin", "name"]);
50
51 let mut conference = Conference {
52 autojoin: get_attr!(root, "autojoin", Default),
53 name: get_attr!(root, "name", Option),
54 nick: None,
55 password: None,
56 extensions: None,
57 };
58
59 for child in root.children().cloned() {
60 if child.is("extensions", ns::BOOKMARKS2) {
61 if conference.extensions.is_some() {
62 return Err(Error::ParseError(
63 "Conference must not have more than one extensions element.",
64 ));
65 }
66 conference.extensions = Some(child.children().cloned().collect());
67 } else if child.is("nick", ns::BOOKMARKS2) {
68 if conference.nick.is_some() {
69 return Err(Error::ParseError(
70 "Conference must not have more than one nick.",
71 ));
72 }
73 check_no_children!(child, "nick");
74 check_no_attributes!(child, "nick");
75 conference.nick = Some(child.text());
76 } else if child.is("password", ns::BOOKMARKS2) {
77 if conference.password.is_some() {
78 return Err(Error::ParseError(
79 "Conference must not have more than one password.",
80 ));
81 }
82 check_no_children!(child, "password");
83 check_no_attributes!(child, "password");
84 conference.password = Some(child.text());
85 }
86 }
87
88 Ok(conference)
89 }
90}
91
92impl From<Conference> for Element {
93 fn from(conference: Conference) -> Element {
94 Element::builder("conference", ns::BOOKMARKS2)
95 .attr("autojoin", conference.autojoin)
96 .attr("name", conference.name)
97 .append_all(
98 conference
99 .nick
100 .map(|nick| Element::builder("nick", ns::BOOKMARKS2).append(nick)),
101 )
102 .append_all(
103 conference
104 .password
105 .map(|password| Element::builder("password", ns::BOOKMARKS2).append(password)),
106 )
107 .append_all(match conference.extensions {
108 Some(extensions) => extensions,
109 None => vec![],
110 })
111 .build()
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use crate::pubsub::pubsub::Item as PubSubItem;
119 use crate::Element;
120 use std::convert::TryFrom;
121
122 #[cfg(target_pointer_width = "32")]
123 #[test]
124 fn test_size() {
125 assert_size!(Conference, 52);
126 }
127
128 #[cfg(target_pointer_width = "64")]
129 #[test]
130 fn test_size() {
131 assert_size!(Conference, 104);
132 }
133
134 #[test]
135 fn simple() {
136 let elem: Element = "<conference xmlns='urn:xmpp:bookmarks:1'/>"
137 .parse()
138 .unwrap();
139 let elem1 = elem.clone();
140 let conference = Conference::try_from(elem).unwrap();
141 assert_eq!(conference.autojoin, Autojoin::False);
142 assert_eq!(conference.name, None);
143 assert_eq!(conference.nick, None);
144 assert_eq!(conference.password, None);
145
146 let elem2 = Element::from(Conference::new());
147 assert_eq!(elem1, elem2);
148 }
149
150 #[test]
151 fn complete() {
152 let elem: Element = "<conference xmlns='urn:xmpp:bookmarks:1' autojoin='true' name='Test MUC'><nick>Coucou</nick><password>secret</password><extensions><test xmlns='urn:xmpp:unknown' /></extensions></conference>".parse().unwrap();
153 let conference = Conference::try_from(elem).unwrap();
154 assert_eq!(conference.autojoin, Autojoin::True);
155 assert_eq!(conference.name, Some(String::from("Test MUC")));
156 assert_eq!(conference.clone().nick.unwrap(), "Coucou");
157 assert_eq!(conference.clone().password.unwrap(), "secret");
158 assert_eq!(conference.clone().extensions.unwrap().len(), 1);
159 assert!(conference.clone().extensions.unwrap()[0].is("test", "urn:xmpp:unknown"));
160 }
161
162 #[test]
163 fn wrapped() {
164 let elem: Element = "<item xmlns='http://jabber.org/protocol/pubsub' id='test-muc@muc.localhost'><conference xmlns='urn:xmpp:bookmarks:1' autojoin='true' name='Test MUC'><nick>Coucou</nick><password>secret</password></conference></item>".parse().unwrap();
165 let item = PubSubItem::try_from(elem).unwrap();
166 let payload = item.payload.clone().unwrap();
167 println!("FOO: payload: {:?}", payload);
168 // let conference = Conference::try_from(payload).unwrap();
169 let conference = Conference::try_from(payload);
170 println!("FOO: conference: {:?}", conference);
171 /*
172 assert_eq!(conference.autojoin, Autojoin::True);
173 assert_eq!(conference.name, Some(String::from("Test MUC")));
174 assert_eq!(conference.clone().nick.unwrap(), "Coucou");
175 assert_eq!(conference.clone().password.unwrap(), "secret");
176
177 let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event'><items node='urn:xmpp:bookmarks:1'><item xmlns='http://jabber.org/protocol/pubsub#event' id='test-muc@muc.localhost'><conference xmlns='urn:xmpp:bookmarks:1' autojoin='true' name='Test MUC'><nick>Coucou</nick><password>secret</password></conference></item></items></event>".parse().unwrap();
178 let mut items = match PubSubEvent::try_from(elem) {
179 Ok(PubSubEvent::PublishedItems { node, items }) => {
180 assert_eq!(&node.0, ns::BOOKMARKS2);
181 items
182 }
183 _ => panic!(),
184 };
185 assert_eq!(items.len(), 1);
186 let item = items.pop().unwrap();
187 let payload = item.payload.clone().unwrap();
188 let conference = Conference::try_from(payload).unwrap();
189 assert_eq!(conference.autojoin, Autojoin::True);
190 assert_eq!(conference.name, Some(String::from("Test MUC")));
191 assert_eq!(conference.clone().nick.unwrap(), "Coucou");
192 assert_eq!(conference.clone().password.unwrap(), "secret");
193 */
194 }
195}