message: Merge body in this module, and make it support xml:lang.

This commit is contained in:
Emmanuel Gil Peyrot 2017-05-06 21:38:23 +01:00
parent 69cfb14c77
commit 4142107965
3 changed files with 53 additions and 117 deletions

View file

@ -1,86 +0,0 @@
// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
//
// 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 minidom::Element;
use error::Error;
use ns;
pub type Body = String;
pub fn parse_body(root: &Element) -> Result<Body, Error> {
// TODO: also support components and servers.
if !root.is("body", ns::JABBER_CLIENT) {
return Err(Error::ParseError("This is not a body element."));
}
for _ in root.children() {
return Err(Error::ParseError("Unknown child in body element."));
}
Ok(root.text())
}
pub fn serialise(body: &Body) -> Element {
Element::builder("body")
.ns(ns::JABBER_CLIENT)
.append(body.to_owned())
.build()
}
#[cfg(test)]
mod tests {
use minidom::Element;
use error::Error;
use body;
use ns;
#[test]
fn test_simple() {
let elem: Element = "<body xmlns='jabber:client'/>".parse().unwrap();
body::parse_body(&elem).unwrap();
}
#[test]
fn test_invalid() {
let elem: Element = "<body xmlns='jabber:server'/>".parse().unwrap();
let error = body::parse_body(&elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
_ => panic!(),
};
assert_eq!(message, "This is not a body element.");
}
#[test]
fn test_invalid_child() {
let elem: Element = "<body xmlns='jabber:client'><coucou/></body>".parse().unwrap();
let error = body::parse_body(&elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
_ => panic!(),
};
assert_eq!(message, "Unknown child in body element.");
}
#[test]
#[ignore]
fn test_invalid_attribute() {
let elem: Element = "<body xmlns='jabber:client' coucou=''/>".parse().unwrap();
let error = body::parse_body(&elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
_ => panic!(),
};
assert_eq!(message, "Unknown attribute in body element.");
}
#[test]
fn test_serialise() {
let body = body::Body::from("Hello world!");
let elem = body::serialise(&body);
assert!(elem.is("body", ns::JABBER_CLIENT));
}
}

View file

@ -37,9 +37,6 @@ pub mod iq;
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
pub mod stanza_error;
/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence
pub mod body;
/// XEP-0004: Data Forms
pub mod data_forms;

View file

@ -6,6 +6,7 @@
use std::convert::TryFrom;
use std::str::FromStr;
use std::collections::BTreeMap;
use minidom::{Element, IntoAttributeValue};
@ -15,7 +16,6 @@ use error::Error;
use ns;
use body;
use stanza_error::StanzaError;
use chatstates::ChatState;
use receipts::Receipt;
@ -27,7 +27,6 @@ use eme::ExplicitMessageEncryption;
/// Lists every known payload of a `<message/>`.
#[derive(Debug, Clone)]
pub enum MessagePayload {
Body(body::Body),
StanzaError(StanzaError),
ChatState(ChatState),
Receipt(Receipt),
@ -86,12 +85,16 @@ pub enum MessagePayloadType {
Parsed(MessagePayload),
}
type Lang = String;
type Body = String;
#[derive(Debug, Clone)]
pub struct Message {
pub from: Option<Jid>,
pub to: Option<Jid>,
pub id: Option<String>,
pub type_: MessageType,
pub bodies: BTreeMap<Lang, Body>,
pub payloads: Vec<MessagePayloadType>,
}
@ -112,37 +115,47 @@ impl<'a> TryFrom<&'a Element> for Message {
Some(type_) => type_.parse()?,
None => Default::default(),
};
let mut bodies = BTreeMap::new();
let mut payloads = vec!();
for elem in root.children() {
let payload = if let Ok(body) = body::parse_body(elem) {
Some(MessagePayload::Body(body))
} else if let Ok(stanza_error) = StanzaError::try_from(elem) {
Some(MessagePayload::StanzaError(stanza_error))
} else if let Ok(chatstate) = ChatState::try_from(elem) {
Some(MessagePayload::ChatState(chatstate))
} else if let Ok(receipt) = Receipt::try_from(elem) {
Some(MessagePayload::Receipt(receipt))
} else if let Ok(delay) = Delay::try_from(elem) {
Some(MessagePayload::Delay(delay))
} else if let Ok(attention) = Attention::try_from(elem) {
Some(MessagePayload::Attention(attention))
} else if let Ok(replace) = Replace::try_from(elem) {
Some(MessagePayload::MessageCorrect(replace))
} else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) {
Some(MessagePayload::ExplicitMessageEncryption(eme))
if elem.is("body", ns::JABBER_CLIENT) {
for _ in elem.children() {
return Err(Error::ParseError("Unknown child in body element."));
}
let lang = elem.attr("xml:lang").unwrap_or("").to_owned();
if let Some(_) = bodies.insert(lang, elem.text()) {
return Err(Error::ParseError("Body element present twice for the same xml:lang."));
}
} else {
None
};
payloads.push(match payload {
Some(payload) => MessagePayloadType::Parsed(payload),
None => MessagePayloadType::XML(elem.clone()),
});
let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) {
Some(MessagePayload::StanzaError(stanza_error))
} else if let Ok(chatstate) = ChatState::try_from(elem) {
Some(MessagePayload::ChatState(chatstate))
} else if let Ok(receipt) = Receipt::try_from(elem) {
Some(MessagePayload::Receipt(receipt))
} else if let Ok(delay) = Delay::try_from(elem) {
Some(MessagePayload::Delay(delay))
} else if let Ok(attention) = Attention::try_from(elem) {
Some(MessagePayload::Attention(attention))
} else if let Ok(replace) = Replace::try_from(elem) {
Some(MessagePayload::MessageCorrect(replace))
} else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) {
Some(MessagePayload::ExplicitMessageEncryption(eme))
} else {
None
};
payloads.push(match payload {
Some(payload) => MessagePayloadType::Parsed(payload),
None => MessagePayloadType::XML(elem.clone()),
});
}
}
Ok(Message {
from: from,
to: to,
id: id,
type_: type_,
bodies: BTreeMap::new(),
payloads: payloads,
})
}
@ -151,7 +164,6 @@ impl<'a> TryFrom<&'a Element> for Message {
impl<'a> Into<Element> for &'a MessagePayload {
fn into(self) -> Element {
match *self {
MessagePayload::Body(ref body) => body::serialise(body),
MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(),
MessagePayload::Attention(ref attention) => attention.into(),
MessagePayload::ChatState(ref chatstate) => chatstate.into(),
@ -171,6 +183,17 @@ impl<'a> Into<Element> for &'a Message {
.attr("to", self.to.clone().and_then(|value| Some(String::from(value))))
.attr("id", self.id.clone())
.attr("type", self.type_.clone())
.append(self.bodies.iter()
.map(|(lang, body)| {
Element::builder("body")
.ns(ns::JABBER_CLIENT)
.attr("xml:lang", match lang.as_ref() {
"" => None,
lang => Some(lang),
})
.append(body.clone())
.build() })
.collect::<Vec<_>>())
.build();
for child in self.payloads.clone() {
let elem = match child {
@ -206,6 +229,7 @@ mod tests {
to: None,
id: None,
type_: MessageType::Normal,
bodies: BTreeMap::new(),
payloads: vec!(),
};
let elem2 = (&message).into();
@ -221,14 +245,15 @@ mod tests {
#[test]
fn test_serialise_body() {
let elem: Element = "<message xmlns='jabber:client' to='coucou@example.org' type='chat'><body>Hello world!</body></message>".parse().unwrap();
let mut bodies = BTreeMap::new();
bodies.insert(String::from(""), String::from("Hello world!"));
let message = Message {
from: None,
to: Some(Jid::from_str("coucou@example.org").unwrap()),
id: None,
type_: MessageType::Chat,
payloads: vec!(
MessagePayloadType::Parsed(MessagePayload::Body("Hello world!".to_owned())),
),
bodies: bodies,
payloads: vec!(),
};
let elem2 = (&message).into();
assert_eq!(elem, elem2);