muc.rs

  1// Copyright (c) 2017 Maxime “pep” Buquet <pep+code@bouah.net>
  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/.
  6
  7use std::convert::TryFrom;
  8
  9use minidom::{Element, IntoElements, ElementEmitter};
 10
 11use error::Error;
 12
 13use ns;
 14
 15#[derive(Debug, Clone)]
 16pub struct Muc;
 17
 18impl TryFrom<Element> for Muc {
 19    type Error = Error;
 20
 21    fn try_from(elem: Element) -> Result<Muc, Error> {
 22        if !elem.is("x", ns::MUC) {
 23            return Err(Error::ParseError("This is not an x element."));
 24        }
 25        for _ in elem.children() {
 26            return Err(Error::ParseError("Unknown child in x element."));
 27        }
 28        for _ in elem.attrs() {
 29            return Err(Error::ParseError("Unknown attribute in x element."));
 30        }
 31        Ok(Muc)
 32    }
 33}
 34
 35impl Into<Element> for Muc {
 36    fn into(self) -> Element {
 37        Element::builder("x")
 38                .ns(ns::MUC)
 39                .build()
 40    }
 41}
 42
 43#[derive(Debug, Clone, PartialEq)]
 44pub enum Status {
 45    // 100
 46    NonAnonymousRoom,
 47
 48    // 101
 49    AffiliationChange,
 50
 51    // 102
 52    ConfigShowsUnavailableMembers,
 53
 54    // 103
 55    ConfigHidesUnavailableMembers,
 56
 57    // 104
 58    ConfigNonPrivacyRelated,
 59
 60    // 110
 61    SelfPresence,
 62
 63    // 170
 64    ConfigRoomLoggingEnabled,
 65
 66    // 171
 67    ConfigRoomLoggingDisabled,
 68
 69    // 172
 70    ConfigRoomNonAnonymous,
 71
 72    // 173
 73    ConfigRoomSemiAnonymous,
 74
 75    // 201
 76    RoomHasBeenCreated,
 77
 78    // 210
 79    AssignedNick,
 80
 81    // 301
 82    Banned,
 83
 84    // 303
 85    NewNick,
 86
 87    // 307
 88    Kicked,
 89
 90    // 321
 91    RemovalFromRoom,
 92
 93    // 322
 94    ConfigMembersOnly,
 95
 96    // 332
 97    ServiceShutdown,
 98}
 99
100impl TryFrom<Element> for Status {
101    type Error = Error;
102
103    fn try_from(elem: Element) -> Result<Status, Error> {
104        if !elem.is("status", ns::MUC_USER) {
105            return Err(Error::ParseError("This is not a status element."));
106        }
107        for _ in elem.children() {
108            return Err(Error::ParseError("Unknown child in status element."));
109        }
110        for (attr, _) in elem.attrs() {
111            if attr != "code" {
112                return Err(Error::ParseError("Unknown attribute in status element."));
113            }
114        }
115        let code = get_attr!(elem, "code", required);
116
117        Ok(match code {
118             100 => Status::NonAnonymousRoom,
119             101 => Status::AffiliationChange,
120             102 => Status::ConfigShowsUnavailableMembers,
121             103 => Status::ConfigHidesUnavailableMembers,
122             104 => Status::ConfigNonPrivacyRelated,
123             110 => Status::SelfPresence,
124             170 => Status::ConfigRoomLoggingEnabled,
125             171 => Status::ConfigRoomLoggingDisabled,
126             172 => Status::ConfigRoomNonAnonymous,
127             173 => Status::ConfigRoomSemiAnonymous,
128             201 => Status::RoomHasBeenCreated,
129             210 => Status::AssignedNick,
130             301 => Status::Banned,
131             303 => Status::NewNick,
132             307 => Status::Kicked,
133             321 => Status::RemovalFromRoom,
134             322 => Status::ConfigMembersOnly,
135             332 => Status::ServiceShutdown,
136             _ => return Err(Error::ParseError("Invalid status code.")),
137        })
138    }
139}
140
141impl Into<Element> for Status {
142    fn into(self) -> Element {
143        Element::builder("status")
144                .ns(ns::MUC_USER)
145                .attr("code", match self {
146                     Status::NonAnonymousRoom => 100,
147                     Status::AffiliationChange => 101,
148                     Status::ConfigShowsUnavailableMembers => 102,
149                     Status::ConfigHidesUnavailableMembers => 103,
150                     Status::ConfigNonPrivacyRelated => 104,
151                     Status::SelfPresence => 110,
152                     Status::ConfigRoomLoggingEnabled => 170,
153                     Status::ConfigRoomLoggingDisabled => 171,
154                     Status::ConfigRoomNonAnonymous => 172,
155                     Status::ConfigRoomSemiAnonymous => 173,
156                     Status::RoomHasBeenCreated => 201,
157                     Status::AssignedNick => 210,
158                     Status::Banned => 301,
159                     Status::NewNick => 303,
160                     Status::Kicked => 307,
161                     Status::RemovalFromRoom => 321,
162                     Status::ConfigMembersOnly => 322,
163                     Status::ServiceShutdown => 332,
164                })
165                .build()
166    }
167}
168
169impl IntoElements for Status {
170    fn into_elements(self, emitter: &mut ElementEmitter) {
171        emitter.append_child(self.into());
172    }
173}
174
175#[derive(Debug, Clone)]
176pub struct MucUser {
177    status: Vec<Status>,
178}
179
180impl TryFrom<Element> for MucUser {
181    type Error = Error;
182
183    fn try_from(elem: Element) -> Result<MucUser, Error> {
184        if !elem.is("x", ns::MUC_USER) {
185            return Err(Error::ParseError("This is not an x element."));
186        }
187        let mut status = vec!();
188        for child in elem.children() {
189            if child.is("status", ns::MUC_USER) {
190                status.push(Status::try_from(child.clone())?);
191            } else {
192                return Err(Error::ParseError("Unknown child in x element."));
193            }
194        }
195        for _ in elem.attrs() {
196            return Err(Error::ParseError("Unknown attribute in x element."));
197        }
198        Ok(MucUser {
199            status: status,
200        })
201    }
202}
203
204impl Into<Element> for MucUser {
205    fn into(self) -> Element {
206        Element::builder("x")
207                .ns(ns::MUC_USER)
208                .append(self.status)
209                .build()
210    }
211}
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216    use std::error::Error as StdError;
217
218    #[test]
219    fn test_muc_simple() {
220        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>".parse().unwrap();
221        Muc::try_from(elem).unwrap();
222    }
223
224    #[test]
225    fn test_muc_invalid_child() {
226        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'><coucou/></x>".parse().unwrap();
227        let error = Muc::try_from(elem).unwrap_err();
228        let message = match error {
229            Error::ParseError(string) => string,
230            _ => panic!(),
231        };
232        assert_eq!(message, "Unknown child in x element.");
233    }
234
235    #[test]
236    fn test_muc_serialise() {
237        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>".parse().unwrap();
238        let muc = Muc;
239        let elem2 = muc.into();
240        assert_eq!(elem, elem2);
241    }
242
243    #[test]
244    fn test_muc_invalid_attribute() {
245        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc' coucou=''/>".parse().unwrap();
246        let error = Muc::try_from(elem).unwrap_err();
247        let message = match error {
248            Error::ParseError(string) => string,
249            _ => panic!(),
250        };
251        assert_eq!(message, "Unknown attribute in x element.");
252    }
253
254    #[test]
255    fn test_muc_user_simple() {
256        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'/>".parse().unwrap();
257        MucUser::try_from(elem).unwrap();
258    }
259
260    #[test]
261    fn test_muc_user_invalid_child() {
262        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'><coucou/></x>".parse().unwrap();
263        let error = MucUser::try_from(elem).unwrap_err();
264        let message = match error {
265            Error::ParseError(string) => string,
266            _ => panic!(),
267        };
268        assert_eq!(message, "Unknown child in x element.");
269    }
270
271    #[test]
272    fn test_muc_user_serialise() {
273        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user'/>".parse().unwrap();
274        let muc = MucUser { status: vec!() };
275        let elem2 = muc.into();
276        assert_eq!(elem, elem2);
277    }
278
279    #[test]
280    fn test_muc_user_invalid_attribute() {
281        let elem: Element = "<x xmlns='http://jabber.org/protocol/muc#user' coucou=''/>".parse().unwrap();
282        let error = MucUser::try_from(elem).unwrap_err();
283        let message = match error {
284            Error::ParseError(string) => string,
285            _ => panic!(),
286        };
287        assert_eq!(message, "Unknown attribute in x element.");
288    }
289
290    #[test]
291    fn test_status_simple() {
292        let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='110'/>".parse().unwrap();
293        Status::try_from(elem).unwrap();
294    }
295
296    #[test]
297    fn test_status_invalid() {
298        let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user'/>".parse().unwrap();
299        let error = Status::try_from(elem).unwrap_err();
300        let message = match error {
301            Error::ParseError(string) => string,
302            _ => panic!(),
303        };
304        assert_eq!(message, "Required attribute 'code' missing.");
305    }
306
307    #[test]
308    fn test_status_invalid_child() {
309        let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='110'><foo/></status>".parse().unwrap();
310        let error = Status::try_from(elem).unwrap_err();
311        let message = match error {
312            Error::ParseError(string) => string,
313            _ => panic!(),
314        };
315        assert_eq!(message, "Unknown child in status element.");
316    }
317
318    #[test]
319    fn test_status_simple_code() {
320        let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='307'/>".parse().unwrap();
321        let status = Status::try_from(elem).unwrap();
322        assert_eq!(status, Status::Kicked);
323    }
324
325    #[test]
326    fn test_status_invalid_code() {
327        let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='666'/>".parse().unwrap();
328        let error = Status::try_from(elem).unwrap_err();
329        let message = match error {
330            Error::ParseError(string) => string,
331            _ => panic!(),
332        };
333        assert_eq!(message, "Invalid status code.");
334    }
335
336    #[test]
337    fn test_status_invalid_code2() {
338        let elem: Element = "<status xmlns='http://jabber.org/protocol/muc#user' code='coucou'/>".parse().unwrap();
339        let error = Status::try_from(elem).unwrap_err();
340        let error = match error {
341            Error::ParseIntError(error) => error,
342            _ => panic!(),
343        };
344        assert_eq!(error.description(), "invalid digit found in string");
345    }
346}