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