// Copyright (c) 2019 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::ns; use crate::util::error::Error; use crate::Element; use std::convert::TryFrom; generate_attribute!( /// Whether a conference bookmark should be joined automatically. Autojoin, "autojoin", bool ); /// A conference bookmark. #[derive(Debug, Clone, Default)] pub struct Conference { /// Whether a conference bookmark should be joined automatically. pub autojoin: Autojoin, /// A user-defined name for this conference. pub name: Option, /// The nick the user will use to join this conference. pub nick: Option, /// The password required to join this conference. pub password: Option, /// Extensions elements. pub extensions: Vec, } impl Conference { /// Create a new conference. pub fn new() -> Conference { Conference::default() } } impl TryFrom for Conference { type Error = Error; fn try_from(root: Element) -> Result { check_self!(root, "conference", BOOKMARKS2, "Conference"); check_no_unknown_attributes!(root, "Conference", ["autojoin", "name"]); let mut conference = Conference { autojoin: get_attr!(root, "autojoin", Default), name: get_attr!(root, "name", Option), nick: None, password: None, extensions: Vec::new(), }; for child in root.children().cloned() { if child.is("nick", ns::BOOKMARKS2) { if conference.nick.is_some() { return Err(Error::ParseError( "Conference must not have more than one nick.", )); } check_no_children!(child, "nick"); check_no_attributes!(child, "nick"); conference.nick = Some(child.text()); } else if child.is("password", ns::BOOKMARKS2) { if conference.password.is_some() { return Err(Error::ParseError( "Conference must not have more than one password.", )); } check_no_children!(child, "password"); check_no_attributes!(child, "password"); conference.password = Some(child.text()); } else if child.is("extensions", ns::BOOKMARKS2) { if !conference.extensions.is_empty() { return Err(Error::ParseError( "Conference must not have more than one extensions element.", )); } conference.extensions.extend(child.children().cloned()); } else { return Err(Error::ParseError( "Unknown element in bookmarks2 conference", )); } } Ok(conference) } } impl From for Element { fn from(conference: Conference) -> Element { Element::builder("conference", ns::BOOKMARKS2) .attr("autojoin", conference.autojoin) .attr("name", conference.name) .append_all( conference .nick .map(|nick| Element::builder("nick", ns::BOOKMARKS2).append(nick)), ) .append_all( conference .password .map(|password| Element::builder("password", ns::BOOKMARKS2).append(password)), ) .append_all(match conference.extensions { empty if empty.is_empty() => None, extensions => { Some(Element::builder("extensions", ns::BOOKMARKS2).append_all(extensions)) } }) .build() } } #[cfg(test)] mod tests { use super::*; use crate::pubsub::{pubsub::Item as PubSubItem, PubSubEvent}; use crate::Element; use std::convert::TryFrom; #[cfg(target_pointer_width = "32")] #[test] fn test_size() { assert_size!(Conference, 52); } #[cfg(target_pointer_width = "64")] #[test] fn test_size() { assert_size!(Conference, 104); } #[test] fn simple() { let elem: Element = "" .parse() .unwrap(); let elem1 = elem.clone(); let conference = Conference::try_from(elem).unwrap(); assert_eq!(conference.autojoin, Autojoin::False); assert_eq!(conference.name, None); assert_eq!(conference.nick, None); assert_eq!(conference.password, None); let elem2 = Element::from(Conference::new()); assert_eq!(elem1, elem2); } #[test] fn complete() { let elem: Element = "Coucousecret".parse().unwrap(); let conference = Conference::try_from(elem).unwrap(); assert_eq!(conference.autojoin, Autojoin::True); assert_eq!(conference.name, Some(String::from("Test MUC"))); assert_eq!(conference.clone().nick.unwrap(), "Coucou"); assert_eq!(conference.clone().password.unwrap(), "secret"); assert_eq!(conference.clone().extensions.len(), 1); assert!(conference.clone().extensions[0].is("test", "urn:xmpp:unknown")); } #[test] fn wrapped() { let elem: Element = "Coucousecret".parse().unwrap(); let item = PubSubItem::try_from(elem).unwrap(); let payload = item.payload.clone().unwrap(); println!("FOO: payload: {:?}", payload); // let conference = Conference::try_from(payload).unwrap(); let conference = Conference::try_from(payload).unwrap(); println!("FOO: conference: {:?}", conference); assert_eq!(conference.autojoin, Autojoin::True); assert_eq!(conference.name, Some(String::from("Test MUC"))); assert_eq!(conference.clone().nick.unwrap(), "Coucou"); assert_eq!(conference.clone().password.unwrap(), "secret"); let elem: Element = "Coucousecret".parse().unwrap(); let mut items = match PubSubEvent::try_from(elem) { Ok(PubSubEvent::PublishedItems { node, items }) => { assert_eq!(&node.0, ns::BOOKMARKS2); items } _ => panic!(), }; assert_eq!(items.len(), 1); let item = items.pop().unwrap(); let payload = item.payload.clone().unwrap(); let conference = Conference::try_from(payload).unwrap(); assert_eq!(conference.autojoin, Autojoin::True); assert_eq!(conference.name, Some(String::from("Test MUC"))); assert_eq!(conference.clone().nick.unwrap(), "Coucou"); assert_eq!(conference.clone().password.unwrap(), "secret"); } }