muc.rs

  1// Copyright (c) 2017 Maxime “pep” Buquet <pep@bouah.net>
  2// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
  3//
  4// This Source Code Form is subject to the terms of the Mozilla Public
  5// License, v. 2.0. If a copy of the MPL was not distributed with this
  6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  7
  8use xso::{AsXml, FromXml};
  9
 10use crate::date::DateTime;
 11use crate::ns;
 12use crate::presence::PresencePayload;
 13
 14/// Represents the query for messages before our join.
 15#[derive(FromXml, AsXml, PartialEq, Debug, Clone, Default)]
 16#[xml(namespace = ns::MUC, name = "history")]
 17pub struct History {
 18    /// How many characters of history to send, in XML characters.
 19    #[xml(attribute(default))]
 20    pub maxchars: Option<u32>,
 21
 22    /// How many messages to send.
 23    #[xml(attribute(default))]
 24    pub maxstanzas: Option<u32>,
 25
 26    /// Only send messages received in these last seconds.
 27    #[xml(attribute(default))]
 28    pub seconds: Option<u32>,
 29
 30    /// Only send messages after this date.
 31    #[xml(attribute(default))]
 32    pub since: Option<DateTime>,
 33}
 34
 35impl History {
 36    /// Create a new empty history element.
 37    pub fn new() -> Self {
 38        History::default()
 39    }
 40
 41    /// Set how many characters of history to send.
 42    pub fn with_maxchars(mut self, maxchars: u32) -> Self {
 43        self.maxchars = Some(maxchars);
 44        self
 45    }
 46
 47    /// Set how many messages to send.
 48    pub fn with_maxstanzas(mut self, maxstanzas: u32) -> Self {
 49        self.maxstanzas = Some(maxstanzas);
 50        self
 51    }
 52
 53    /// Only send messages received in these last seconds.
 54    pub fn with_seconds(mut self, seconds: u32) -> Self {
 55        self.seconds = Some(seconds);
 56        self
 57    }
 58
 59    /// Only send messages received since this date.
 60    pub fn with_since(mut self, since: DateTime) -> Self {
 61        self.since = Some(since);
 62        self
 63    }
 64}
 65
 66/// Represents a room join request.
 67#[derive(FromXml, AsXml, PartialEq, Debug, Clone, Default)]
 68#[xml(namespace = ns::MUC, name = "x")]
 69pub struct Muc {
 70    /// Password to use when the room is protected by a password.
 71    #[xml(extract(default, fields(text(type_ = String))))]
 72    pub password: Option<String>,
 73
 74    /// Controls how much and how old we want to receive history on join.
 75    #[xml(child(default))]
 76    pub history: Option<History>,
 77}
 78
 79impl PresencePayload for Muc {}
 80
 81impl Muc {
 82    /// Create a new MUC join element.
 83    pub fn new() -> Self {
 84        Muc::default()
 85    }
 86
 87    /// Join a room with this password.
 88    pub fn with_password(mut self, password: String) -> Self {
 89        self.password = Some(password);
 90        self
 91    }
 92
 93    /// Join a room with only that much history.
 94    pub fn with_history(mut self, history: History) -> Self {
 95        self.history = Some(history);
 96        self
 97    }
 98}
 99
100#[cfg(test)]
101mod tests {
102    use super::*;
103    use core::str::FromStr;
104    use minidom::Element;
105    use xso::error::{Error, FromElementError};
106
107    #[cfg(target_pointer_width = "32")]
108    #[test]
109    fn test_size() {
110        assert_size!(History, 40);
111        assert_size!(Muc, 52);
112    }
113
114    #[cfg(target_pointer_width = "64")]
115    #[test]
116    fn test_size() {
117        assert_size!(History, 40);
118        assert_size!(Muc, 64);
119    }
120
121    #[test]
122    fn test_muc_simple() {
123        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>"
124            .parse()
125            .unwrap();
126        Muc::try_from(elem).unwrap();
127    }
128
129    #[test]
130    #[cfg_attr(feature = "disable-validation", should_panic = "Result::unwrap_err")]
131    fn test_muc_invalid_child() {
132        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'><coucou/></x>"
133            .parse()
134            .unwrap();
135        let error = Muc::try_from(elem).unwrap_err();
136        let message = match error {
137            FromElementError::Invalid(Error::Other(string)) => string,
138            _ => panic!(),
139        };
140        assert_eq!(message, "Unknown child in Muc element.");
141    }
142
143    #[test]
144    fn test_muc_serialise() {
145        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>"
146            .parse()
147            .unwrap();
148        let muc = Muc {
149            password: None,
150            history: None,
151        };
152        let elem2 = muc.into();
153        assert_eq!(elem, elem2);
154    }
155
156    #[cfg(not(feature = "disable-validation"))]
157    #[test]
158    fn test_muc_invalid_attribute() {
159        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc' coucou=''/>"
160            .parse()
161            .unwrap();
162        let error = Muc::try_from(elem).unwrap_err();
163        let message = match error {
164            FromElementError::Invalid(Error::Other(string)) => string,
165            _ => panic!(),
166        };
167        assert_eq!(message, "Unknown attribute in Muc element.");
168    }
169
170    #[test]
171    fn test_muc_simple_password() {
172        let elem: Element =
173            "<x xmlns='http://jabber.org/protocol/muc'><password>coucou</password></x>"
174                .parse()
175                .unwrap();
176        let elem1 = elem.clone();
177        let muc = Muc::try_from(elem).unwrap();
178        assert_eq!(muc.password, Some("coucou".to_owned()));
179
180        let elem2 = Element::from(muc);
181        assert_eq!(elem1, elem2);
182    }
183
184    #[test]
185    fn history() {
186        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'>
187                <history maxstanzas='0'/>
188            </x>"
189            .parse()
190            .unwrap();
191        let muc = Muc::try_from(elem).unwrap();
192        let muc2 = Muc::new().with_history(History::new().with_maxstanzas(0));
193        assert_eq!(muc, muc2);
194
195        let history = muc.history.unwrap();
196        assert_eq!(history.maxstanzas, Some(0));
197        assert_eq!(history.maxchars, None);
198        assert_eq!(history.seconds, None);
199        assert_eq!(history.since, None);
200
201        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'>
202                <history since='1970-01-01T00:00:00Z'/>
203            </x>"
204            .parse()
205            .unwrap();
206        let muc = Muc::try_from(elem).unwrap();
207        assert_eq!(
208            muc.history.unwrap().since.unwrap(),
209            DateTime::from_str("1970-01-01T00:00:00+00:00").unwrap()
210        );
211    }
212}