bookmarks2.rs

  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: 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: Vec::new(),
 57        };
 58
 59        for child in root.children().cloned() {
 60            if child.is("nick", ns::BOOKMARKS2) {
 61                if conference.nick.is_some() {
 62                    return Err(Error::ParseError(
 63                        "Conference must not have more than one nick.",
 64                    ));
 65                }
 66                check_no_children!(child, "nick");
 67                check_no_attributes!(child, "nick");
 68                conference.nick = Some(child.text());
 69            } else if child.is("password", ns::BOOKMARKS2) {
 70                if conference.password.is_some() {
 71                    return Err(Error::ParseError(
 72                        "Conference must not have more than one password.",
 73                    ));
 74                }
 75                check_no_children!(child, "password");
 76                check_no_attributes!(child, "password");
 77                conference.password = Some(child.text());
 78            } else if child.is("extensions", ns::BOOKMARKS2) {
 79                if !conference.extensions.is_empty() {
 80                    return Err(Error::ParseError(
 81                        "Conference must not have more than one extensions element.",
 82                    ));
 83                }
 84                conference.extensions.extend(child.children().cloned());
 85            } else {
 86                return Err(Error::ParseError(
 87                    "Unknown element in bookmarks2 conference",
 88                ));
 89            }
 90        }
 91
 92        Ok(conference)
 93    }
 94}
 95
 96impl From<Conference> for Element {
 97    fn from(conference: Conference) -> Element {
 98        Element::builder("conference", ns::BOOKMARKS2)
 99            .attr("autojoin", conference.autojoin)
100            .attr("name", conference.name)
101            .append_all(
102                conference
103                    .nick
104                    .map(|nick| Element::builder("nick", ns::BOOKMARKS2).append(nick)),
105            )
106            .append_all(
107                conference
108                    .password
109                    .map(|password| Element::builder("password", ns::BOOKMARKS2).append(password)),
110            )
111            .append_all(match conference.extensions {
112                empty if empty.is_empty() => None,
113                extensions => {
114                    Some(Element::builder("extensions", ns::BOOKMARKS2).append_all(extensions))
115                }
116            })
117            .build()
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124    use crate::pubsub::{pubsub::Item as PubSubItem, PubSubEvent};
125    use crate::Element;
126    use std::convert::TryFrom;
127
128    #[cfg(target_pointer_width = "32")]
129    #[test]
130    fn test_size() {
131        assert_size!(Conference, 52);
132    }
133
134    #[cfg(target_pointer_width = "64")]
135    #[test]
136    fn test_size() {
137        assert_size!(Conference, 104);
138    }
139
140    #[test]
141    fn simple() {
142        let elem: Element = "<conference xmlns='urn:xmpp:bookmarks:1'/>"
143            .parse()
144            .unwrap();
145        let elem1 = elem.clone();
146        let conference = Conference::try_from(elem).unwrap();
147        assert_eq!(conference.autojoin, Autojoin::False);
148        assert_eq!(conference.name, None);
149        assert_eq!(conference.nick, None);
150        assert_eq!(conference.password, None);
151
152        let elem2 = Element::from(Conference::new());
153        assert_eq!(elem1, elem2);
154    }
155
156    #[test]
157    fn complete() {
158        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();
159        let conference = Conference::try_from(elem).unwrap();
160        assert_eq!(conference.autojoin, Autojoin::True);
161        assert_eq!(conference.name, Some(String::from("Test MUC")));
162        assert_eq!(conference.clone().nick.unwrap(), "Coucou");
163        assert_eq!(conference.clone().password.unwrap(), "secret");
164        assert_eq!(conference.clone().extensions.len(), 1);
165        assert!(conference.clone().extensions[0].is("test", "urn:xmpp:unknown"));
166    }
167
168    #[test]
169    fn wrapped() {
170        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();
171        let item = PubSubItem::try_from(elem).unwrap();
172        let payload = item.payload.clone().unwrap();
173        println!("FOO: payload: {:?}", payload);
174        // let conference = Conference::try_from(payload).unwrap();
175        let conference = Conference::try_from(payload).unwrap();
176        println!("FOO: conference: {:?}", conference);
177        assert_eq!(conference.autojoin, Autojoin::True);
178        assert_eq!(conference.name, Some(String::from("Test MUC")));
179        assert_eq!(conference.clone().nick.unwrap(), "Coucou");
180        assert_eq!(conference.clone().password.unwrap(), "secret");
181
182        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();
183        let mut items = match PubSubEvent::try_from(elem) {
184            Ok(PubSubEvent::PublishedItems { node, items }) => {
185                assert_eq!(&node.0, ns::BOOKMARKS2);
186                items
187            }
188            _ => panic!(),
189        };
190        assert_eq!(items.len(), 1);
191        let item = items.pop().unwrap();
192        let payload = item.payload.clone().unwrap();
193        let conference = Conference::try_from(payload).unwrap();
194        assert_eq!(conference.autojoin, Autojoin::True);
195        assert_eq!(conference.name, Some(String::from("Test MUC")));
196        assert_eq!(conference.clone().nick.unwrap(), "Coucou");
197        assert_eq!(conference.clone().password.unwrap(), "secret");
198    }
199}