jingle_message.rs

  1// Copyright (c) 2017 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/.
  6
  7use try_from::TryFrom;
  8
  9use minidom::Element;
 10
 11use error::Error;
 12
 13use jingle::SessionId;
 14
 15use ns;
 16
 17#[derive(Debug, Clone)]
 18pub enum JingleMI {
 19    Propose {
 20        sid: SessionId,
 21        // TODO: Use a more specialised type here.
 22        description: Element,
 23    },
 24    Retract(SessionId),
 25    Accept(SessionId),
 26    Proceed(SessionId),
 27    Reject(SessionId),
 28}
 29
 30fn get_sid(elem: Element) -> Result<SessionId, Error> {
 31    check_no_unknown_attributes!(elem, "Jingle message", ["id"]);
 32    Ok(SessionId(get_attr!(elem, "id", required)))
 33}
 34
 35fn check_empty_and_get_sid(elem: Element) -> Result<SessionId, Error> {
 36    check_no_children!(elem, "Jingle message");
 37    get_sid(elem)
 38}
 39
 40impl TryFrom<Element> for JingleMI {
 41    type Err = Error;
 42
 43    fn try_from(elem: Element) -> Result<JingleMI, Error> {
 44        if !elem.has_ns(ns::JINGLE_MESSAGE) {
 45            return Err(Error::ParseError("This is not a Jingle message element."));
 46        }
 47        Ok(match elem.name() {
 48            "propose" => {
 49                let mut description = None;
 50                for child in elem.children() {
 51                    if child.name() != "description" {
 52                        return Err(Error::ParseError("Unknown child in propose element."));
 53                    }
 54                    if description.is_some() {
 55                        return Err(Error::ParseError("Too many children in propose element."));
 56                    }
 57                    description = Some(child.clone());
 58                }
 59                JingleMI::Propose {
 60                    sid: get_sid(elem)?,
 61                    description: description.ok_or(Error::ParseError("Propose element doesn’t contain a description."))?,
 62                }
 63            },
 64            "retract" => JingleMI::Retract(check_empty_and_get_sid(elem)?),
 65            "accept" => JingleMI::Accept(check_empty_and_get_sid(elem)?),
 66            "proceed" => JingleMI::Proceed(check_empty_and_get_sid(elem)?),
 67            "reject" => JingleMI::Reject(check_empty_and_get_sid(elem)?),
 68            _ => return Err(Error::ParseError("This is not a Jingle message element.")),
 69        })
 70    }
 71}
 72
 73impl From<JingleMI> for Element {
 74    fn from(jingle_mi: JingleMI) -> Element {
 75        match jingle_mi {
 76            JingleMI::Propose { sid, description } => {
 77                Element::builder("propose")
 78                        .ns(ns::JINGLE_MESSAGE)
 79                        .attr("id", sid)
 80                        .append(description)
 81            },
 82            JingleMI::Retract(sid) => {
 83                Element::builder("retract")
 84                        .ns(ns::JINGLE_MESSAGE)
 85                        .attr("id", sid)
 86            }
 87            JingleMI::Accept(sid) => {
 88                Element::builder("accept")
 89                        .ns(ns::JINGLE_MESSAGE)
 90                        .attr("id", sid)
 91            }
 92            JingleMI::Proceed(sid) => {
 93                Element::builder("proceed")
 94                        .ns(ns::JINGLE_MESSAGE)
 95                        .attr("id", sid)
 96            }
 97            JingleMI::Reject(sid) => {
 98                Element::builder("reject")
 99                        .ns(ns::JINGLE_MESSAGE)
100                        .attr("id", sid)
101            }
102        }.build()
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn test_simple() {
112        let elem: Element = "<accept xmlns='urn:xmpp:jingle-message:0' id='coucou'/>".parse().unwrap();
113        JingleMI::try_from(elem).unwrap();
114    }
115
116    #[test]
117    fn test_invalid_child() {
118        let elem: Element = "<propose xmlns='urn:xmpp:jingle-message:0' id='coucou'><coucou/></propose>".parse().unwrap();
119        let error = JingleMI::try_from(elem).unwrap_err();
120        let message = match error {
121            Error::ParseError(string) => string,
122            _ => panic!(),
123        };
124        assert_eq!(message, "Unknown child in propose element.");
125    }
126}