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    for _ in elem.children() {
 37        return Err(Error::ParseError("Unknown child in Jingle message element."));
 38    }
 39    get_sid(elem)
 40}
 41
 42impl TryFrom<Element> for JingleMI {
 43    type Err = Error;
 44
 45    fn try_from(elem: Element) -> Result<JingleMI, Error> {
 46        if !elem.has_ns(ns::JINGLE_MESSAGE) {
 47            return Err(Error::ParseError("This is not a Jingle message element."));
 48        }
 49        Ok(match elem.name() {
 50            "propose" => {
 51                let mut description = None;
 52                for child in elem.children() {
 53                    if child.name() != "description" {
 54                        return Err(Error::ParseError("Unknown child in propose element."));
 55                    }
 56                    if description.is_some() {
 57                        return Err(Error::ParseError("Too many children in propose element."));
 58                    }
 59                    description = Some(child.clone());
 60                }
 61                JingleMI::Propose {
 62                    sid: get_sid(elem)?,
 63                    description: description.ok_or(Error::ParseError("Propose element doesn’t contain a description."))?,
 64                }
 65            },
 66            "retract" => JingleMI::Retract(check_empty_and_get_sid(elem)?),
 67            "accept" => JingleMI::Accept(check_empty_and_get_sid(elem)?),
 68            "proceed" => JingleMI::Proceed(check_empty_and_get_sid(elem)?),
 69            "reject" => JingleMI::Reject(check_empty_and_get_sid(elem)?),
 70            _ => return Err(Error::ParseError("This is not a Jingle message element.")),
 71        })
 72    }
 73}
 74
 75impl From<JingleMI> for Element {
 76    fn from(jingle_mi: JingleMI) -> Element {
 77        match jingle_mi {
 78            JingleMI::Propose { sid, description } => {
 79                Element::builder("propose")
 80                        .ns(ns::JINGLE_MESSAGE)
 81                        .attr("id", sid)
 82                        .append(description)
 83            },
 84            JingleMI::Retract(sid) => {
 85                Element::builder("retract")
 86                        .ns(ns::JINGLE_MESSAGE)
 87                        .attr("id", sid)
 88            }
 89            JingleMI::Accept(sid) => {
 90                Element::builder("accept")
 91                        .ns(ns::JINGLE_MESSAGE)
 92                        .attr("id", sid)
 93            }
 94            JingleMI::Proceed(sid) => {
 95                Element::builder("proceed")
 96                        .ns(ns::JINGLE_MESSAGE)
 97                        .attr("id", sid)
 98            }
 99            JingleMI::Reject(sid) => {
100                Element::builder("reject")
101                        .ns(ns::JINGLE_MESSAGE)
102                        .attr("id", sid)
103            }
104        }.build()
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn test_simple() {
114        let elem: Element = "<accept xmlns='urn:xmpp:jingle-message:0' id='coucou'/>".parse().unwrap();
115        JingleMI::try_from(elem).unwrap();
116    }
117
118    #[test]
119    fn test_invalid_child() {
120        let elem: Element = "<propose xmlns='urn:xmpp:jingle-message:0' id='coucou'><coucou/></propose>".parse().unwrap();
121        let error = JingleMI::try_from(elem).unwrap_err();
122        let message = match error {
123            Error::ParseError(string) => string,
124            _ => panic!(),
125        };
126        assert_eq!(message, "Unknown child in propose element.");
127    }
128}