1
0
mirror of https://gitlab.com/xmpp-rs/xmpp-rs.git synced 2024-06-26 08:58:27 +02:00

Run cargo fmt.

This commit is contained in:
Emmanuel Gil Peyrot 2018-12-18 15:32:05 +01:00
parent d517b8e32e
commit efd7bd5f2f
51 changed files with 2283 additions and 1331 deletions

View File

@ -8,7 +8,9 @@ use crate::message::MessagePayload;
generate_empty_element!(
/// Requests the attention of the recipient.
Attention, "attention", ATTENTION
Attention,
"attention",
ATTENTION
);
impl MessagePayload for Attention {}
@ -16,9 +18,9 @@ impl MessagePayload for Attention {}
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use minidom::Element;
use try_from::TryFrom;
#[test]
fn test_size() {
@ -33,7 +35,9 @@ mod tests {
#[test]
fn test_invalid_child() {
let elem: Element = "<attention xmlns='urn:xmpp:attention:0'><coucou/></attention>".parse().unwrap();
let elem: Element = "<attention xmlns='urn:xmpp:attention:0'><coucou/></attention>"
.parse()
.unwrap();
let error = Attention::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -44,7 +48,9 @@ mod tests {
#[test]
fn test_invalid_attribute() {
let elem: Element = "<attention xmlns='urn:xmpp:attention:0' coucou=''/>".parse().unwrap();
let elem: Element = "<attention xmlns='urn:xmpp:attention:0' coucou=''/>"
.parse()
.unwrap();
let error = Attention::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,

View File

@ -4,16 +4,14 @@
// 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 crate::error::Error;
use crate::iq::{IqResultPayload, IqSetPayload};
use crate::ns;
use jid::Jid;
use minidom::Element;
use std::str::FromStr;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use jid::Jid;
use crate::ns;
use crate::iq::{IqSetPayload, IqResultPayload};
/// The request for resource binding, which is the process by which a client
/// can obtain a full JID and start exchanging on the XMPP network.
///
@ -74,23 +72,16 @@ impl TryFrom<Element> for Bind {
impl From<Bind> for Element {
fn from(bind: Bind) -> Element {
Element::builder("bind")
.ns(ns::BIND)
.append(match bind {
Bind::None => vec!(),
Bind::Resource(resource) => vec!(
Element::builder("resource")
.ns(ns::BIND)
.append(resource)
.build()
),
Bind::Jid(jid) => vec!(
Element::builder("jid")
.ns(ns::BIND)
.append(jid)
.build()
),
})
.build()
.ns(ns::BIND)
.append(match bind {
Bind::None => vec![],
Bind::Resource(resource) => vec![Element::builder("resource")
.ns(ns::BIND)
.append(resource)
.build()],
Bind::Jid(jid) => vec![Element::builder("jid").ns(ns::BIND).append(jid).build()],
})
.build()
}
}
@ -112,7 +103,9 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>".parse().unwrap();
let elem: Element = "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>"
.parse()
.unwrap();
let bind = Bind::try_from(elem).unwrap();
assert_eq!(bind, Bind::None);
}

View File

@ -4,20 +4,19 @@
// 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 try_from::TryFrom;
use crate::error::Error;
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
use crate::ns;
use jid::Jid;
use minidom::Element;
use crate::error::Error;
use crate::ns;
use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload};
use try_from::TryFrom;
generate_empty_element!(
/// The element requesting the blocklist, the result iq will contain a
/// [BlocklistResult].
BlocklistRequest, "blocklist", BLOCKING
BlocklistRequest,
"blocklist",
BLOCKING
);
impl IqGetPayload for BlocklistRequest {}
@ -67,7 +66,8 @@ macro_rules! generate_blocking_element {
generate_blocking_element!(
/// The element containing the current blocklist, as a reply from
/// [BlocklistRequest].
BlocklistResult, "blocklist"
BlocklistResult,
"blocklist"
);
impl IqResultPayload for BlocklistResult {}
@ -75,7 +75,8 @@ impl IqResultPayload for BlocklistResult {}
// TODO: Prevent zero elements from being allowed.
generate_blocking_element!(
/// A query to block one or more JIDs.
Block, "block"
Block,
"block"
);
impl IqSetPayload for Block {}
@ -84,14 +85,17 @@ generate_blocking_element!(
/// A query to unblock one or more JIDs, or all of them.
///
/// Warning: not putting any JID there means clearing out the blocklist.
Unblock, "unblock"
Unblock,
"unblock"
);
impl IqSetPayload for Unblock {}
generate_empty_element!(
/// The application-specific error condition when a message is blocked.
Blocked, "blocked", BLOCKING_ERRORS
Blocked,
"blocked",
BLOCKING_ERRORS
);
#[cfg(test)]
@ -138,7 +142,7 @@ mod tests {
#[test]
fn test_items() {
let elem: Element = "<blocklist xmlns='urn:xmpp:blocking'><item jid='coucou@coucou'/><item jid='domain'/></blocklist>".parse().unwrap();
let two_items = vec!(
let two_items = vec![
Jid {
node: Some(String::from("coucou")),
domain: String::from("coucou"),
@ -149,7 +153,7 @@ mod tests {
domain: String::from("domain"),
resource: None,
},
);
];
let request_elem = elem.clone();
let error = BlocklistRequest::try_from(request_elem).unwrap_err();
@ -174,7 +178,9 @@ mod tests {
#[test]
fn test_invalid() {
let elem: Element = "<blocklist xmlns='urn:xmpp:blocking' coucou=''/>".parse().unwrap();
let elem: Element = "<blocklist xmlns='urn:xmpp:blocking' coucou=''/>"
.parse()
.unwrap();
let request_elem = elem.clone();
let error = BlocklistRequest::try_from(request_elem).unwrap_err();
let message = match error {
@ -191,7 +197,9 @@ mod tests {
};
assert_eq!(message, "Unknown attribute in blocklist element.");
let elem: Element = "<block xmlns='urn:xmpp:blocking' coucou=''/>".parse().unwrap();
let elem: Element = "<block xmlns='urn:xmpp:blocking' coucou=''/>"
.parse()
.unwrap();
let error = Block::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -199,7 +207,9 @@ mod tests {
};
assert_eq!(message, "Unknown attribute in block element.");
let elem: Element = "<unblock xmlns='urn:xmpp:blocking' coucou=''/>".parse().unwrap();
let elem: Element = "<unblock xmlns='urn:xmpp:blocking' coucou=''/>"
.parse()
.unwrap();
let error = Unblock::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,

View File

@ -8,7 +8,9 @@ use jid::Jid;
generate_attribute!(
/// Whether a conference bookmark should be joined automatically.
Autojoin, "autojoin", bool
Autojoin,
"autojoin",
bool
);
generate_element!(
@ -70,9 +72,9 @@ impl Storage {
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::compare_elements::NamespaceAwareCompare;
use minidom::Element;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -111,7 +113,10 @@ mod tests {
assert_eq!(storage.urls[0].url, "https://example.org/");
assert_eq!(storage.conferences.len(), 1);
assert_eq!(storage.conferences[0].autojoin, Autojoin::True);
assert_eq!(storage.conferences[0].jid, Jid::bare("test-muc", "muc.localhost"));
assert_eq!(
storage.conferences[0].jid,
Jid::bare("test-muc", "muc.localhost")
);
assert_eq!(storage.conferences[0].name, "Test MUC");
assert_eq!(storage.conferences[0].clone().nick.unwrap(), "Coucou");
assert_eq!(storage.conferences[0].clone().password.unwrap(), "secret");

View File

@ -4,23 +4,20 @@
// 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 try_from::TryFrom;
use crate::presence::PresencePayload;
use crate::disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery};
use crate::data_forms::DataForm;
use crate::hashes::{Hash, Algo};
use minidom::Element;
use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity};
use crate::error::Error;
use crate::hashes::{Algo, Hash};
use crate::ns;
use crate::presence::PresencePayload;
use base64;
use blake2::VarBlake2b;
use digest::{Digest, Input, VariableOutput};
use minidom::Element;
use sha1::Sha1;
use sha2::{Sha256, Sha512};
use sha3::{Sha3_256, Sha3_512};
use blake2::VarBlake2b;
use digest::{Digest, VariableOutput, Input};
use try_from::TryFrom;
/// Represents a capability hash for a given client.
#[derive(Debug, Clone)]
@ -65,12 +62,12 @@ impl TryFrom<Element> for Caps {
impl From<Caps> for Element {
fn from(caps: Caps) -> Element {
Element::builder("c")
.ns(ns::CAPS)
.attr("ext", caps.ext)
.attr("hash", caps.hash.algo)
.attr("node", caps.node)
.attr("ver", base64::encode(&caps.hash.hash))
.build()
.ns(ns::CAPS)
.attr("ext", caps.ext)
.attr("hash", caps.hash.algo)
.attr("node", caps.node)
.attr("ver", base64::encode(&caps.hash.hash))
.build()
}
}
@ -81,8 +78,8 @@ fn compute_item(field: &str) -> Vec<u8> {
}
fn compute_items<T, F: Fn(&T) -> Vec<u8>>(things: &[T], encode: F) -> Vec<u8> {
let mut string: Vec<u8> = vec!();
let mut accumulator: Vec<Vec<u8>> = vec!();
let mut string: Vec<u8> = vec![];
let mut accumulator: Vec<Vec<u8>> = vec![];
for thing in things {
let bytes = encode(thing);
accumulator.push(bytes);
@ -114,7 +111,7 @@ fn compute_identities(identities: &[Identity]) -> Vec<u8> {
fn compute_extensions(extensions: &[DataForm]) -> Vec<u8> {
compute_items(extensions, |extension| {
let mut bytes = vec!();
let mut bytes = vec![];
// TODO: maybe handle the error case?
if let Some(ref form_type) = extension.form_type {
bytes.extend_from_slice(form_type.as_bytes());
@ -125,8 +122,9 @@ fn compute_extensions(extensions: &[DataForm]) -> Vec<u8> {
continue;
}
bytes.append(&mut compute_item(&field.var));
bytes.append(&mut compute_items(&field.values,
|value| compute_item(value)));
bytes.append(&mut compute_items(&field.values, |value| {
compute_item(value)
}));
}
bytes
})
@ -143,7 +141,7 @@ pub fn compute_disco(disco: &DiscoInfoResult) -> Vec<u8> {
let features_string = compute_features(&disco.features);
let extensions_string = compute_extensions(&disco.extensions);
let mut final_string = vec!();
let mut final_string = vec![];
final_string.extend(identities_string);
final_string.extend(features_string);
final_string.extend(extensions_string);
@ -164,33 +162,33 @@ pub fn hash_caps(data: &[u8], algo: Algo) -> Result<Hash, String> {
Algo::Sha_1 => {
let hash = Sha1::digest(data);
get_hash_vec(hash.as_slice())
},
}
Algo::Sha_256 => {
let hash = Sha256::digest(data);
get_hash_vec(hash.as_slice())
},
}
Algo::Sha_512 => {
let hash = Sha512::digest(data);
get_hash_vec(hash.as_slice())
},
}
Algo::Sha3_256 => {
let hash = Sha3_256::digest(data);
get_hash_vec(hash.as_slice())
},
}
Algo::Sha3_512 => {
let hash = Sha3_512::digest(data);
get_hash_vec(hash.as_slice())
},
}
Algo::Blake2b_256 => {
let mut hasher = VarBlake2b::new(32).unwrap();
hasher.input(data);
hasher.vec_result()
},
}
Algo::Blake2b_512 => {
let mut hasher = VarBlake2b::new(64).unwrap();
hasher.input(data);
hasher.vec_result()
},
}
Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)),
},
algo: algo,
@ -229,7 +227,10 @@ mod tests {
let caps = Caps::try_from(elem).unwrap();
assert_eq!(caps.node, String::from("coucou"));
assert_eq!(caps.hash.algo, Algo::Sha_256);
assert_eq!(caps.hash.hash, base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap());
assert_eq!(
caps.hash.hash,
base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap()
);
}
#[test]
@ -262,8 +263,10 @@ mod tests {
<feature var='http://jabber.org/protocol/disco#items'/>
<feature var='http://jabber.org/protocol/muc'/>
</query>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let data = b"client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<";
let mut expected = Vec::with_capacity(data.len());
expected.extend_from_slice(data);
@ -272,7 +275,10 @@ mod tests {
assert_eq!(caps, expected);
let sha_1 = caps::hash_caps(&caps, Algo::Sha_1).unwrap();
assert_eq!(sha_1.hash, base64::decode("QgayPKawpkPSDYmwT/WM94uAlu0=").unwrap());
assert_eq!(
sha_1.hash,
base64::decode("QgayPKawpkPSDYmwT/WM94uAlu0=").unwrap()
);
}
#[test]
@ -308,7 +314,9 @@ mod tests {
</field>
</x>
</query>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let data = b"client/pc/el/\xce\xa8 0.11<client/pc/en/Psi 0.11<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<urn:xmpp:dataforms:softwareinfo<ip_version<ipv4<ipv6<os<Mac<os_version<10.5.1<software<Psi<software_version<0.11<";
let mut expected = Vec::with_capacity(data.len());
expected.extend_from_slice(data);
@ -317,6 +325,9 @@ mod tests {
assert_eq!(caps, expected);
let sha_1 = caps::hash_caps(&caps, Algo::Sha_1).unwrap();
assert_eq!(sha_1.hash, base64::decode("q07IKJEyjvHSyhy//CH0CxmKi8w=").unwrap());
assert_eq!(
sha_1.hash,
base64::decode("q07IKJEyjvHSyhy//CH0CxmKi8w=").unwrap()
);
}
}

View File

@ -32,10 +32,10 @@ impl MessagePayload for ChatState {}
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use crate::ns;
use minidom::Element;
use try_from::TryFrom;
#[test]
fn test_size() {
@ -44,13 +44,17 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<active xmlns='http://jabber.org/protocol/chatstates'/>".parse().unwrap();
let elem: Element = "<active xmlns='http://jabber.org/protocol/chatstates'/>"
.parse()
.unwrap();
ChatState::try_from(elem).unwrap();
}
#[test]
fn test_invalid() {
let elem: Element = "<coucou xmlns='http://jabber.org/protocol/chatstates'/>".parse().unwrap();
let elem: Element = "<coucou xmlns='http://jabber.org/protocol/chatstates'/>"
.parse()
.unwrap();
let error = ChatState::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -61,7 +65,9 @@ mod tests {
#[test]
fn test_invalid_child() {
let elem: Element = "<gone xmlns='http://jabber.org/protocol/chatstates'><coucou/></gone>".parse().unwrap();
let elem: Element = "<gone xmlns='http://jabber.org/protocol/chatstates'><coucou/></gone>"
.parse()
.unwrap();
let error = ChatState::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -72,7 +78,9 @@ mod tests {
#[test]
fn test_invalid_attribute() {
let elem: Element = "<inactive xmlns='http://jabber.org/protocol/chatstates' coucou=''/>".parse().unwrap();
let elem: Element = "<inactive xmlns='http://jabber.org/protocol/chatstates' coucou=''/>"
.parse()
.unwrap();
let error = ChatState::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,

View File

@ -4,7 +4,7 @@
// 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::{Node, Element};
use minidom::{Element, Node};
pub trait NamespaceAwareCompare {
/// Namespace-aware comparison for tests
@ -14,10 +14,10 @@ pub trait NamespaceAwareCompare {
impl NamespaceAwareCompare for Node {
fn compare_to(&self, other: &Self) -> bool {
match (self, other) {
(&Node::Element(ref elem1), &Node::Element(ref elem2)) =>
Element::compare_to(elem1, elem2),
(&Node::Text(ref text1), &Node::Text(ref text2)) =>
text1 == text2,
(&Node::Element(ref elem1), &Node::Element(ref elem2)) => {
Element::compare_to(elem1, elem2)
}
(&Node::Text(ref text1), &Node::Text(ref text2)) => text1 == text2,
_ => false,
}
}
@ -25,20 +25,21 @@ impl NamespaceAwareCompare for Node {
impl NamespaceAwareCompare for Element {
fn compare_to(&self, other: &Self) -> bool {
if self.name() == other.name() &&
self.ns() == other.ns() &&
self.attrs().eq(other.attrs())
if self.name() == other.name() && self.ns() == other.ns() && self.attrs().eq(other.attrs())
{
let child_elems = self.children().count();
let text_is_whitespace = self.texts()
let text_is_whitespace = self
.texts()
.all(|text| text.chars().all(char::is_whitespace));
if child_elems > 0 && text_is_whitespace {
// Ignore all the whitespace text nodes
self.children().zip(other.children())
self.children()
.zip(other.children())
.all(|(node1, node2)| node1.compare_to(node2))
} else {
// Compare with text nodes
self.nodes().zip(other.nodes())
self.nodes()
.zip(other.nodes())
.all(|(node1, node2)| node1.compare_to(node2))
}
} else {

View File

@ -5,8 +5,8 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::helpers::PlainText;
use sha1::Sha1;
use digest::Digest;
use sha1::Sha1;
generate_element!(
/// The main authentication mechanism for components.
@ -25,9 +25,7 @@ generate_element!(
impl Handshake {
/// Creates a successful reply from a server.
pub fn new() -> Handshake {
Handshake {
data: None,
}
Handshake { data: None }
}
/// Creates an authentication request from the component.
@ -44,8 +42,8 @@ impl Handshake {
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -61,11 +59,15 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<handshake xmlns='jabber:component:accept'/>".parse().unwrap();
let elem: Element = "<handshake xmlns='jabber:component:accept'/>"
.parse()
.unwrap();
let handshake = Handshake::try_from(elem).unwrap();
assert_eq!(handshake.data, None);
let elem: Element = "<handshake xmlns='jabber:component:accept'>Coucou</handshake>".parse().unwrap();
let elem: Element = "<handshake xmlns='jabber:component:accept'>Coucou</handshake>"
.parse()
.unwrap();
let handshake = Handshake::try_from(elem).unwrap();
assert_eq!(handshake.data, Some(String::from("Coucou")));
}
@ -76,6 +78,9 @@ mod tests {
assert_eq!(handshake.data, None);
let handshake = Handshake::from_password_and_stream_id("123456", "sid");
assert_eq!(handshake.data, Some(String::from("9accec263ab84a43c6037ccf7cd48cb1d3f6df8e")));
assert_eq!(
handshake.data,
Some(String::from("9accec263ab84a43c6037ccf7cd48cb1d3f6df8e"))
);
}
}

View File

@ -4,14 +4,11 @@
// 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 try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use crate::ns;
use crate::media_element::MediaElement;
use crate::ns;
use minidom::Element;
use try_from::TryFrom;
generate_element!(
/// Represents one of the possible values for a list- field.
@ -98,8 +95,7 @@ pub struct Field {
impl Field {
fn is_list(&self) -> bool {
self.type_ == FieldType::ListSingle ||
self.type_ == FieldType::ListMulti
self.type_ == FieldType::ListSingle || self.type_ == FieldType::ListMulti
}
}
@ -114,9 +110,9 @@ impl TryFrom<Element> for Field {
type_: get_attr!(elem, "type", default),
label: get_attr!(elem, "label", optional),
required: false,
options: vec!(),
values: vec!(),
media: vec!(),
options: vec![],
values: vec![],
media: vec![],
};
for element in elem.children() {
if element.is("value", ns::DATA_FORMS) {
@ -140,7 +136,9 @@ impl TryFrom<Element> for Field {
let media_element = MediaElement::try_from(element.clone())?;
field.media.push(media_element);
} else {
return Err(Error::ParseError("Field child isnt a value, option or media element."));
return Err(Error::ParseError(
"Field child isnt a value, option or media element.",
));
}
}
Ok(field)
@ -150,17 +148,30 @@ impl TryFrom<Element> for Field {
impl From<Field> for Element {
fn from(field: Field) -> Element {
Element::builder("field")
.ns(ns::DATA_FORMS)
.attr("var", field.var)
.attr("type", field.type_)
.attr("label", field.label)
.append(if field.required { Some(Element::builder("required").ns(ns::DATA_FORMS).build()) } else { None })
.append(field.options)
.append(field.values.into_iter().map(|value| {
Element::builder("value").ns(ns::DATA_FORMS).append(value).build()
}).collect::<Vec<_>>())
.append(field.media)
.build()
.ns(ns::DATA_FORMS)
.attr("var", field.var)
.attr("type", field.type_)
.attr("label", field.label)
.append(if field.required {
Some(Element::builder("required").ns(ns::DATA_FORMS).build())
} else {
None
})
.append(field.options)
.append(
field
.values
.into_iter()
.map(|value| {
Element::builder("value")
.ns(ns::DATA_FORMS)
.append(value)
.build()
})
.collect::<Vec<_>>(),
)
.append(field.media)
.build()
}
}
@ -215,7 +226,7 @@ impl TryFrom<Element> for DataForm {
form_type: None,
title: None,
instructions: None,
fields: vec!(),
fields: vec![],
};
for child in elem.children() {
if child.is("title", ns::DATA_FORMS) {
@ -227,7 +238,9 @@ impl TryFrom<Element> for DataForm {
form.title = Some(child.text());
} else if child.is("instructions", ns::DATA_FORMS) {
if form.instructions.is_some() {
return Err(Error::ParseError("More than one instructions in form element."));
return Err(Error::ParseError(
"More than one instructions in form element.",
));
}
check_no_children!(child, "instructions");
check_no_attributes!(child, "instructions");
@ -255,12 +268,19 @@ impl TryFrom<Element> for DataForm {
impl From<DataForm> for Element {
fn from(form: DataForm) -> Element {
Element::builder("x")
.ns(ns::DATA_FORMS)
.attr("type", form.type_)
.append(form.title.map(|title| Element::builder("title").ns(ns::DATA_FORMS).append(title)))
.append(form.instructions.map(|text| Element::builder("instructions").ns(ns::DATA_FORMS).append(text)))
.append(form.fields)
.build()
.ns(ns::DATA_FORMS)
.attr("type", form.type_)
.append(
form.title
.map(|title| Element::builder("title").ns(ns::DATA_FORMS).append(title)),
)
.append(form.instructions.map(|text| {
Element::builder("instructions")
.ns(ns::DATA_FORMS)
.append(text)
}))
.append(form.fields)
.build()
}
}
@ -318,7 +338,9 @@ mod tests {
#[test]
fn test_wrong_child() {
let elem: Element = "<x xmlns='jabber:x:data' type='cancel'><coucou/></x>".parse().unwrap();
let elem: Element = "<x xmlns='jabber:x:data' type='cancel'><coucou/></x>"
.parse()
.unwrap();
let error = DataForm::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -329,12 +351,17 @@ mod tests {
#[test]
fn option() {
let elem: Element = "<option xmlns='jabber:x:data' label='Coucou!'><value>coucou</value></option>".parse().unwrap();
let elem: Element =
"<option xmlns='jabber:x:data' label='Coucou!'><value>coucou</value></option>"
.parse()
.unwrap();
let option = Option_::try_from(elem).unwrap();
assert_eq!(&option.label.unwrap(), "Coucou!");
assert_eq!(&option.value, "coucou");
let elem: Element = "<option xmlns='jabber:x:data' label='Coucou!'/>".parse().unwrap();
let elem: Element = "<option xmlns='jabber:x:data' label='Coucou!'/>"
.parse()
.unwrap();
let error = Option_::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -348,6 +375,9 @@ mod tests {
Error::ParseError(string) => string,
_ => panic!(),
};
assert_eq!(message, "Element option must not have more than one value child.");
assert_eq!(
message,
"Element option must not have more than one value child."
);
}
}

View File

@ -4,12 +4,10 @@
// 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 std::str::FromStr;
use minidom::{IntoAttributeValue, IntoElements, ElementEmitter};
use chrono::{DateTime as ChronoDateTime, FixedOffset};
use crate::error::Error;
use chrono::{DateTime as ChronoDateTime, FixedOffset};
use minidom::{ElementEmitter, IntoAttributeValue, IntoElements};
use std::str::FromStr;
/// Implements the DateTime profile of XEP-0082, which represents a
/// non-recurring moment in time, with an accuracy of seconds or fraction of
@ -114,7 +112,8 @@ mod tests {
#[test]
fn test_serialise() {
let date = DateTime(ChronoDateTime::parse_from_rfc3339("2017-05-21T20:19:55+01:00").unwrap());
let date =
DateTime(ChronoDateTime::parse_from_rfc3339("2017-05-21T20:19:55+01:00").unwrap());
let attr = date.into_attribute_value();
assert_eq!(attr, Some(String::from("2017-05-21T20:19:55+01:00")));
}

View File

@ -4,14 +4,12 @@
// 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 crate::date::DateTime;
use crate::helpers::PlainText;
use crate::message::MessagePayload;
use crate::presence::PresencePayload;
use crate::date::DateTime;
use jid::Jid;
use crate::helpers::PlainText;
generate_element!(
/// Notes when and by whom a message got stored for later delivery.
Delay, "delay", DELAY,
@ -34,10 +32,10 @@ impl PresencePayload for Delay {}
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use minidom::Element;
use std::str::FromStr;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -53,16 +51,24 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<delay xmlns='urn:xmpp:delay' from='capulet.com' stamp='2002-09-10T23:08:25Z'/>".parse().unwrap();
let elem: Element =
"<delay xmlns='urn:xmpp:delay' from='capulet.com' stamp='2002-09-10T23:08:25Z'/>"
.parse()
.unwrap();
let delay = Delay::try_from(elem).unwrap();
assert_eq!(delay.from, Some(Jid::from_str("capulet.com").unwrap()));
assert_eq!(delay.stamp, DateTime::from_str("2002-09-10T23:08:25Z").unwrap());
assert_eq!(
delay.stamp,
DateTime::from_str("2002-09-10T23:08:25Z").unwrap()
);
assert_eq!(delay.data, None);
}
#[test]
fn test_unknown() {
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>".parse().unwrap();
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
.parse()
.unwrap();
let error = Delay::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -73,7 +79,9 @@ mod tests {
#[test]
fn test_invalid_child() {
let elem: Element = "<delay xmlns='urn:xmpp:delay'><coucou/></delay>".parse().unwrap();
let elem: Element = "<delay xmlns='urn:xmpp:delay'><coucou/></delay>"
.parse()
.unwrap();
let error = Delay::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -84,7 +92,9 @@ mod tests {
#[test]
fn test_serialise() {
let elem: Element = "<delay xmlns='urn:xmpp:delay' stamp='2002-09-10T23:08:25+00:00'/>".parse().unwrap();
let elem: Element = "<delay xmlns='urn:xmpp:delay' stamp='2002-09-10T23:08:25+00:00'/>"
.parse()
.unwrap();
let delay = Delay {
from: None,
stamp: DateTime::from_str("2002-09-10T23:08:25Z").unwrap(),

View File

@ -4,16 +4,13 @@
// 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 try_from::TryFrom;
use minidom::Element;
use jid::Jid;
use crate::error::Error;
use crate::ns;
use crate::iq::{IqGetPayload, IqResultPayload};
use crate::data_forms::{DataForm, DataFormType};
use crate::error::Error;
use crate::iq::{IqGetPayload, IqResultPayload};
use crate::ns;
use jid::Jid;
use minidom::Element;
use try_from::TryFrom;
generate_element!(
/// Structure representing a `<query xmlns='http://jabber.org/protocol/disco#info'/>` element.
@ -59,16 +56,24 @@ impl TryFrom<Element> for Identity {
fn try_from(elem: Element) -> Result<Identity, Error> {
check_self!(elem, "identity", DISCO_INFO, "disco#info identity");
check_no_children!(elem, "disco#info identity");
check_no_unknown_attributes!(elem, "disco#info identity", ["category", "type", "xml:lang", "name"]);
check_no_unknown_attributes!(
elem,
"disco#info identity",
["category", "type", "xml:lang", "name"]
);
let category = get_attr!(elem, "category", required);
if category == "" {
return Err(Error::ParseError("Identity must have a non-empty 'category' attribute."))
return Err(Error::ParseError(
"Identity must have a non-empty 'category' attribute.",
));
}
let type_ = get_attr!(elem, "type", required);
if type_ == "" {
return Err(Error::ParseError("Identity must have a non-empty 'type' attribute."))
return Err(Error::ParseError(
"Identity must have a non-empty 'type' attribute.",
));
}
Ok(Identity {
@ -83,12 +88,12 @@ impl TryFrom<Element> for Identity {
impl From<Identity> for Element {
fn from(identity: Identity) -> Element {
Element::builder("identity")
.ns(ns::DISCO_INFO)
.attr("category", identity.category)
.attr("type", identity.type_)
.attr("xml:lang", identity.lang)
.attr("name", identity.name)
.build()
.ns(ns::DISCO_INFO)
.attr("category", identity.category)
.attr("type", identity.type_)
.attr("xml:lang", identity.lang)
.attr("name", identity.name)
.build()
}
}
@ -122,9 +127,9 @@ impl TryFrom<Element> for DiscoInfoResult {
let mut result = DiscoInfoResult {
node: get_attr!(elem, "node", optional),
identities: vec!(),
features: vec!(),
extensions: vec!(),
identities: vec![],
features: vec![],
extensions: vec![],
};
for child in elem.children() {
@ -137,7 +142,9 @@ impl TryFrom<Element> for DiscoInfoResult {
} else if child.is("x", ns::DATA_FORMS) {
let data_form = DataForm::try_from(child.clone())?;
if data_form.type_ != DataFormType::Result_ {
return Err(Error::ParseError("Data form must have a 'result' type in disco#info."));
return Err(Error::ParseError(
"Data form must have a 'result' type in disco#info.",
));
}
if data_form.form_type.is_none() {
return Err(Error::ParseError("Data form found without a FORM_TYPE."));
@ -149,13 +156,21 @@ impl TryFrom<Element> for DiscoInfoResult {
}
if result.identities.is_empty() {
return Err(Error::ParseError("There must be at least one identity in disco#info."));
return Err(Error::ParseError(
"There must be at least one identity in disco#info.",
));
}
if result.features.is_empty() {
return Err(Error::ParseError("There must be at least one feature in disco#info."));
return Err(Error::ParseError(
"There must be at least one feature in disco#info.",
));
}
if !result.features.contains(&Feature { var: ns::DISCO_INFO.to_owned() }) {
return Err(Error::ParseError("disco#info feature not present in disco#info."));
if !result.features.contains(&Feature {
var: ns::DISCO_INFO.to_owned(),
}) {
return Err(Error::ParseError(
"disco#info feature not present in disco#info.",
));
}
Ok(result)
@ -165,12 +180,12 @@ impl TryFrom<Element> for DiscoInfoResult {
impl From<DiscoInfoResult> for Element {
fn from(disco: DiscoInfoResult) -> Element {
Element::builder("query")
.ns(ns::DISCO_INFO)
.attr("node", disco.node)
.append(disco.identities)
.append(disco.features)
.append(disco.extensions)
.build()
.ns(ns::DISCO_INFO)
.attr("node", disco.node)
.append(disco.identities)
.append(disco.features)
.append(disco.extensions)
.build()
}
}
@ -295,7 +310,10 @@ mod tests {
#[test]
fn test_invalid() {
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><coucou/></query>".parse().unwrap();
let elem: Element =
"<query xmlns='http://jabber.org/protocol/disco#info'><coucou/></query>"
.parse()
.unwrap();
let error = DiscoInfoResult::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -306,7 +324,10 @@ mod tests {
#[test]
fn test_invalid_identity() {
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity/></query>".parse().unwrap();
let elem: Element =
"<query xmlns='http://jabber.org/protocol/disco#info'><identity/></query>"
.parse()
.unwrap();
let error = DiscoInfoResult::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -314,13 +335,19 @@ mod tests {
};
assert_eq!(message, "Required attribute 'category' missing.");
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category=''/></query>".parse().unwrap();
let elem: Element =
"<query xmlns='http://jabber.org/protocol/disco#info'><identity category=''/></query>"
.parse()
.unwrap();
let error = DiscoInfoResult::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
_ => panic!(),
};
assert_eq!(message, "Identity must have a non-empty 'category' attribute.");
assert_eq!(
message,
"Identity must have a non-empty 'category' attribute."
);
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='coucou'/></query>".parse().unwrap();
let error = DiscoInfoResult::try_from(elem).unwrap_err();
@ -341,7 +368,10 @@ mod tests {
#[test]
fn test_invalid_feature() {
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><feature/></query>".parse().unwrap();
let elem: Element =
"<query xmlns='http://jabber.org/protocol/disco#info'><feature/></query>"
.parse()
.unwrap();
let error = DiscoInfoResult::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -352,13 +382,18 @@ mod tests {
#[test]
fn test_invalid_result() {
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'/>".parse().unwrap();
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'/>"
.parse()
.unwrap();
let error = DiscoInfoResult::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
_ => panic!(),
};
assert_eq!(message, "There must be at least one identity in disco#info.");
assert_eq!(
message,
"There must be at least one identity in disco#info."
);
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='client' type='pc'/></query>".parse().unwrap();
let error = DiscoInfoResult::try_from(elem).unwrap_err();
@ -379,23 +414,31 @@ mod tests {
#[test]
fn test_simple_items() {
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>".parse().unwrap();
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>"
.parse()
.unwrap();
let query = DiscoItemsQuery::try_from(elem).unwrap();
assert!(query.node.is_none());
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items' node='coucou'/>".parse().unwrap();
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items' node='coucou'/>"
.parse()
.unwrap();
let query = DiscoItemsQuery::try_from(elem).unwrap();
assert_eq!(query.node, Some(String::from("coucou")));
}
#[test]
fn test_simple_items_result() {
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>".parse().unwrap();
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>"
.parse()
.unwrap();
let query = DiscoItemsResult::try_from(elem).unwrap();
assert!(query.node.is_none());
assert!(query.items.is_empty());
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items' node='coucou'/>".parse().unwrap();
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#items' node='coucou'/>"
.parse()
.unwrap();
let query = DiscoItemsResult::try_from(elem).unwrap();
assert_eq!(query.node, Some(String::from("coucou")));
assert!(query.items.is_empty());

View File

@ -4,18 +4,16 @@
// 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 crate::presence::PresencePayload;
use crate::disco::{Feature, Identity, DiscoInfoResult, DiscoInfoQuery};
use crate::data_forms::DataForm;
use crate::hashes::{Hash, Algo};
use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity};
use crate::hashes::{Algo, Hash};
use crate::ns;
use crate::presence::PresencePayload;
use base64;
use blake2::VarBlake2b;
use digest::{Digest, Input, VariableOutput};
use sha2::{Sha256, Sha512};
use sha3::{Sha3_256, Sha3_512};
use blake2::VarBlake2b;
use digest::{Digest, VariableOutput, Input};
generate_element!(
/// Represents a set of capability hashes, all of them must correspond to
@ -37,8 +35,8 @@ fn compute_item(field: &str) -> Vec<u8> {
}
fn compute_items<T, F: Fn(&T) -> Vec<u8>>(things: &[T], separator: u8, encode: F) -> Vec<u8> {
let mut string: Vec<u8> = vec!();
let mut accumulator: Vec<Vec<u8>> = vec!();
let mut string: Vec<u8> = vec![];
let mut accumulator: Vec<Vec<u8>> = vec![];
for thing in things {
let bytes = encode(thing);
accumulator.push(bytes);
@ -60,8 +58,12 @@ fn compute_identities(identities: &[Identity]) -> Vec<u8> {
compute_items(identities, 0x1c, |identity| {
let mut bytes = compute_item(&identity.category);
bytes.append(&mut compute_item(&identity.type_));
bytes.append(&mut compute_item(&identity.lang.clone().unwrap_or_default()));
bytes.append(&mut compute_item(&identity.name.clone().unwrap_or_default()));
bytes.append(&mut compute_item(
&identity.lang.clone().unwrap_or_default(),
));
bytes.append(&mut compute_item(
&identity.name.clone().unwrap_or_default(),
));
bytes.push(0x1e);
bytes
})
@ -71,8 +73,9 @@ fn compute_extensions(extensions: &[DataForm]) -> Vec<u8> {
compute_items(extensions, 0x1c, |extension| {
compute_items(&extension.fields, 0x1d, |field| {
let mut bytes = compute_item(&field.var);
bytes.append(&mut compute_items(&field.values, 0x1e,
|value| compute_item(value)));
bytes.append(&mut compute_items(&field.values, 0x1e, |value| {
compute_item(value)
}));
bytes
})
})
@ -86,7 +89,7 @@ pub fn compute_disco(disco: &DiscoInfoResult) -> Vec<u8> {
let identities_string = compute_identities(&disco.identities);
let extensions_string = compute_extensions(&disco.extensions);
let mut final_string = vec!();
let mut final_string = vec![];
final_string.extend(features_string);
final_string.extend(identities_string);
final_string.extend(extensions_string);
@ -107,29 +110,29 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result<Hash, String> {
Algo::Sha_256 => {
let hash = Sha256::digest(data);
get_hash_vec(hash.as_slice())
},
}
Algo::Sha_512 => {
let hash = Sha512::digest(data);
get_hash_vec(hash.as_slice())
},
}
Algo::Sha3_256 => {
let hash = Sha3_256::digest(data);
get_hash_vec(hash.as_slice())
},
}
Algo::Sha3_512 => {
let hash = Sha3_512::digest(data);
get_hash_vec(hash.as_slice())
},
}
Algo::Blake2b_256 => {
let mut hasher = VarBlake2b::new(32).unwrap();
hasher.input(data);
hasher.vec_result()
},
}
Algo::Blake2b_512 => {
let mut hasher = VarBlake2b::new(64).unwrap();
hasher.input(data);
hasher.vec_result()
},
}
Algo::Sha_1 => return Err(String::from("Disabled algorithm sha-1: unsafe.")),
Algo::Unknown(algo) => return Err(format!("Unknown algorithm: {}.", algo)),
},
@ -141,16 +144,21 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result<Hash, String> {
/// ecaps2 hash.
pub fn query_ecaps2(hash: Hash) -> DiscoInfoQuery {
DiscoInfoQuery {
node: Some(format!("{}#{}.{}", ns::ECAPS2, String::from(hash.algo), base64::encode(&hash.hash))),
node: Some(format!(
"{}#{}.{}",
ns::ECAPS2,
String::from(hash.algo),
base64::encode(&hash.hash)
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use minidom::Element;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -170,9 +178,15 @@ mod tests {
let ecaps2 = ECaps2::try_from(elem).unwrap();
assert_eq!(ecaps2.hashes.len(), 2);
assert_eq!(ecaps2.hashes[0].algo, Algo::Sha_256);
assert_eq!(ecaps2.hashes[0].hash, base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap());
assert_eq!(
ecaps2.hashes[0].hash,
base64::decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=").unwrap()
);
assert_eq!(ecaps2.hashes[1].algo, Algo::Sha3_256);
assert_eq!(ecaps2.hashes[1].hash, base64::decode("+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=").unwrap());
assert_eq!(
ecaps2.hashes[1].hash,
base64::decode("+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=").unwrap()
);
}
#[test]
@ -217,50 +231,52 @@ mod tests {
<feature var="jabber:iq:roster"/>
<feature var="jabber:iq:last"/>
</query>
"#.parse().unwrap();
let expected = vec![104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109, 115, 31,
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 104,
97, 116, 115, 116, 97, 116, 101, 115, 31, 104, 116, 116, 112, 58,
47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105,
110, 102, 111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
108, 47, 100, 105, 115, 99, 111, 35, 105, 116, 101, 109, 115, 31,
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98,
98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47,
114, 111, 115, 116, 101, 114, 120, 31, 104, 116, 116, 112, 58, 47,
47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112,
58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 47, 112, 114, 111,
102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116, 114, 97, 110,
115, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
58, 108, 97, 115, 116, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
58, 112, 114, 105, 118, 97, 99, 121, 31, 106, 97, 98, 98, 101, 114,
58, 105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97, 98,
98, 101, 114, 58, 105, 113, 58, 116, 105, 109, 101, 31, 106, 97,
98, 98, 101, 114, 58, 105, 113, 58, 118, 101, 114, 115, 105, 111,
110, 31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 111, 111, 98, 31,
117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31,
117, 114, 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105,
112, 116, 115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116,
105, 109, 101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 109, 111,
98, 105, 108, 101, 31, 31, 66, 111, 109, 98, 117, 115, 77, 111,
100, 31, 30, 28, 28];
"#
.parse()
.unwrap();
let expected = vec![
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
114, 111, 116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109,
115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103,
47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116,
101, 115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105,
110, 102, 111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35,
105, 116, 101, 109, 115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, 98, 31, 104,
116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
111, 116, 111, 99, 111, 108, 47, 114, 111, 115, 116, 101, 114, 120, 31, 104, 116, 116,
112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,
111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105,
47, 112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116, 114, 97, 110,
115, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116,
31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 112, 114, 105, 118, 97, 99, 121, 31,
106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97,
98, 98, 101, 114, 58, 105, 113, 58, 116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114,
58, 105, 113, 58, 118, 101, 114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101, 114, 58,
120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110,
103, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116,
115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109, 101, 31, 28, 99,
108, 105, 101, 110, 116, 31, 109, 111, 98, 105, 108, 101, 31, 31, 66, 111, 109, 98,
117, 115, 77, 111, 100, 31, 30, 28, 28,
];
let disco = DiscoInfoResult::try_from(elem).unwrap();
let ecaps2 = compute_disco(&disco);
assert_eq!(ecaps2.len(), 0x1d9);
assert_eq!(ecaps2, expected);
let sha_256 = hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap();
assert_eq!(sha_256.hash, base64::decode("kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8=").unwrap());
assert_eq!(
sha_256.hash,
base64::decode("kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8=").unwrap()
);
let sha3_256 = hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap();
assert_eq!(sha3_256.hash, base64::decode("79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q=").unwrap());
assert_eq!(
sha3_256.hash,
base64::decode("79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q=").unwrap()
);
}
#[test]
@ -329,121 +345,111 @@ mod tests {
</field>
</x>
</query>
"#.parse().unwrap();
let expected = vec![103, 97, 109, 101, 115, 58, 98, 111, 97, 114, 100,
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46,
111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 97,
99, 116, 105, 118, 105, 116, 121, 31, 104, 116, 116, 112, 58, 47,
47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
111, 116, 111, 99, 111, 108, 47, 97, 99, 116, 105, 118, 105, 116,
121, 43, 110, 111, 116, 105, 102, 121, 31, 104, 116, 116, 112, 58,
47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
111, 116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114,
101, 97, 109, 115, 31, 104, 116, 116, 112, 58,47, 47, 106, 97, 98,
98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99,
111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116, 101, 115, 31,
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 111,
109, 109, 97, 110, 100, 115, 31,104,116, 116, 112, 58, 47, 47, 106,
97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,
111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 110, 102,
111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47,
100, 105, 115, 99, 111, 35, 105, 116, 101, 109, 115, 31, 104, 116,
116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103,
47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 101, 118, 105, 108,
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46,
111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 102,
101, 97, 116, 117, 114, 101, 45, 110, 101, 103, 31, 104, 116, 116,
112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
112, 114, 111, 116, 111, 99, 111, 108, 47, 103, 101, 111, 108, 111,
99, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99,111, 108, 47,
103, 101, 111, 108, 111, 99, 43, 110, 111, 116, 105, 102, 121, 31,
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
114, 103,47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98,
98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
46, 111, 114, 103, 47, 112, 114, 111,116, 111, 99, 111, 108, 47,
105, 113, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97,
98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,111,
99, 111, 108, 47, 109, 111, 111, 100, 31, 104, 116, 116, 112, 58,
47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
111, 116, 111, 99, 111,108, 47, 109, 111, 111, 100, 43, 110, 111,
116, 105, 102, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97,
98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111,
99, 111, 108, 47, 114, 111, 115, 116, 101, 114, 120, 31, 104, 116,
116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103,
47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104,
116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 47,
112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116,
114, 97, 110, 115, 102, 101, 114, 31, 104, 116, 116, 112, 58, 47,
47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
111, 116, 111, 99, 111, 108, 47, 116, 117, 110, 101, 31, 104, 116,
116, 112, 58, 47, 47, 119, 119, 119, 46, 102, 97, 99, 101, 98, 111,
111, 107, 46, 99, 111, 109, 47, 120, 109, 112, 112, 47, 109, 101,
115, 115, 97, 103, 101, 115, 31, 104, 116, 116, 112, 58, 47, 47,
119, 119, 119, 46, 120, 109, 112, 112, 46, 111, 114, 103, 47, 101,
120, 116, 101, 110, 115, 105, 111, 110, 115, 47, 120, 101, 112, 45,
48, 48, 56, 52, 46, 104, 116, 109, 108, 35, 110, 115, 45, 109, 101,
116, 97, 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121, 31,
106, 97, 98, 98, 101, 114,58, 105,113, 58, 97, 118, 97, 116, 97,
114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 98, 114, 111,
119, 115, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
100, 116, 99, 112, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
102, 105, 108, 101, 120, 102, 101, 114, 31, 106, 97, 98, 98, 101,
114, 58, 105, 113, 58, 105, 98, 98, 31, 106, 97, 98, 98, 101, 114,
58, 105, 113, 58, 105, 110, 98, 97, 110, 100, 31, 106, 97, 98, 98,
101, 114, 58, 105, 113, 58, 106, 105, 100, 108, 105, 110, 107, 31,
106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116, 31,
106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 111, 111, 98, 31, 106,
97,98, 98, 101, 114, 58, 105, 113, 58, 112, 114, 105, 118, 97, 99,
121, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 114, 111,
115, 116, 101, 114,31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
58, 118, 101, 114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101,
114, 58, 120, 58, 100, 97, 116, 97, 31, 106, 97, 98, 98, 101, 114,
58, 120, 58, 101, 118, 101, 110, 116, 31, 106, 97, 98, 98, 101,
114, 58, 120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109,
112, 112, 58, 97, 118, 97, 116, 97, 114, 58, 109, 101, 116, 97,
100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121,31, 117, 114,
110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31, 117, 114,
110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116,
115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109,
101, 31, 28, 99, 108, 105, 101, 110, 116, 31, 112, 99, 31, 101,
110, 31, 84, 107, 97, 98, 98, 101, 114,31, 30, 99, 108, 105, 101,
110, 116, 31, 112, 99, 31, 114, 117, 31, 208, 162, 208, 186, 208,
176, 208, 177, 208, 177, 208, 181, 209, 128, 31, 30, 28, 70, 79,
82, 77, 95, 84, 89, 80, 69, 31, 117, 114, 110, 58, 120, 109, 112,
112, 58, 100, 97, 116, 97, 102, 111, 114, 109, 115, 58, 115, 111,
102, 116, 119, 97, 114, 101,105, 110, 102, 111, 31, 30, 111, 115,
31, 87, 105, 110, 100, 111, 119, 115, 31, 30, 111, 115, 95, 118,
101, 114, 115, 105, 111, 110, 31, 88, 80, 31, 30, 115, 111, 102,
116, 119, 97, 114, 101, 31, 84, 107, 97, 98, 98, 101, 114, 31, 30,
115, 111, 102, 116, 119, 97, 114, 101, 95, 118, 101, 114, 115, 105,
111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50,
48, 49, 49, 49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108,
47, 84, 107, 32, 56, 46,54, 98, 50, 41, 31, 30, 29, 28];
"#
.parse()
.unwrap();
let expected = vec![
103, 97, 109, 101, 115, 58, 98, 111, 97, 114, 100, 31, 104, 116, 116, 112, 58, 47, 47,
106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
108, 47, 97, 99, 116, 105, 118, 105, 116, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106,
97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47,
97, 99, 116, 105, 118, 105, 116, 121, 43, 110, 111, 116, 105, 102, 121, 31, 104, 116,
116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111,
116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109, 115, 31,
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
114, 111, 116, 111, 99, 111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116, 101, 115,
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 111, 109, 109, 97, 110, 100, 115, 31,
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 110, 102, 111,
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
112, 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 116, 101,
109, 115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 101, 118, 105, 108, 31, 104, 116,
116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111,
116, 111, 99, 111, 108, 47, 102, 101, 97, 116, 117, 114, 101, 45, 110, 101, 103, 31,
104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
114, 111, 116, 111, 99, 111, 108, 47, 103, 101, 111, 108, 111, 99, 31, 104, 116, 116,
112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,
111, 99, 111, 108, 47, 103, 101, 111, 108, 111, 99, 43, 110, 111, 116, 105, 102, 121,
31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47,
47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
108, 47, 105, 113, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 109, 111,
111, 100, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 109, 111, 111, 100, 43, 110, 111,
116, 105, 102, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46,
111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 114, 111, 115, 116, 101,
114, 120, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112,
58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111,
99, 111, 108, 47, 115, 105, 47, 112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108,
101, 45, 116, 114, 97, 110, 115, 102, 101, 114, 31, 104, 116, 116, 112, 58, 47, 47,
106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
108, 47, 116, 117, 110, 101, 31, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46,
102, 97, 99, 101, 98, 111, 111, 107, 46, 99, 111, 109, 47, 120, 109, 112, 112, 47, 109,
101, 115, 115, 97, 103, 101, 115, 31, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119,
46, 120, 109, 112, 112, 46, 111, 114, 103, 47, 101, 120, 116, 101, 110, 115, 105, 111,
110, 115, 47, 120, 101, 112, 45, 48, 48, 56, 52, 46, 104, 116, 109, 108, 35, 110, 115,
45, 109, 101, 116, 97, 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121, 31, 106, 97,
98, 98, 101, 114, 58, 105, 113, 58, 97, 118, 97, 116, 97, 114, 31, 106, 97, 98, 98,
101, 114, 58, 105, 113, 58, 98, 114, 111, 119, 115, 101, 31, 106, 97, 98, 98, 101, 114,
58, 105, 113, 58, 100, 116, 99, 112, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
102, 105, 108, 101, 120, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
58, 105, 98, 98, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 105, 110, 98, 97,
110, 100, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 106, 105, 100, 108, 105,
110, 107, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116, 31, 106,
97, 98, 98, 101, 114, 58, 105, 113, 58, 111, 111, 98, 31, 106, 97, 98, 98, 101, 114,
58, 105, 113, 58, 112, 114, 105, 118, 97, 99, 121, 31, 106, 97, 98, 98, 101, 114, 58,
105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105,
113, 58, 116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 118, 101,
114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 100, 97, 116, 97,
31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 101, 118, 101, 110, 116, 31, 106, 97, 98,
98, 101, 114, 58, 120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58,
97, 118, 97, 116, 97, 114, 58, 109, 101, 116, 97, 100, 97, 116, 97, 43, 110, 111, 116,
105, 102, 121, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31,
117, 114, 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116, 115, 31,
117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109, 101, 31, 28, 99, 108, 105,
101, 110, 116, 31, 112, 99, 31, 101, 110, 31, 84, 107, 97, 98, 98, 101, 114, 31, 30,
99, 108, 105, 101, 110, 116, 31, 112, 99, 31, 114, 117, 31, 208, 162, 208, 186, 208,
176, 208, 177, 208, 177, 208, 181, 209, 128, 31, 30, 28, 70, 79, 82, 77, 95, 84, 89,
80, 69, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 100, 97, 116, 97, 102, 111, 114,
109, 115, 58, 115, 111, 102, 116, 119, 97, 114, 101, 105, 110, 102, 111, 31, 30, 111,
115, 31, 87, 105, 110, 100, 111, 119, 115, 31, 30, 111, 115, 95, 118, 101, 114, 115,
105, 111, 110, 31, 88, 80, 31, 30, 115, 111, 102, 116, 119, 97, 114, 101, 31, 84, 107,
97, 98, 98, 101, 114, 31, 30, 115, 111, 102, 116, 119, 97, 114, 101, 95, 118, 101, 114,
115, 105, 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, 48, 49, 49,
49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46, 54,
98, 50, 41, 31, 30, 29, 28,
];
let disco = DiscoInfoResult::try_from(elem).unwrap();
let ecaps2 = compute_disco(&disco);
assert_eq!(ecaps2.len(), 0x543);
assert_eq!(ecaps2, expected);
let sha_256 = hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap();
assert_eq!(sha_256.hash, base64::decode("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=").unwrap());
assert_eq!(
sha_256.hash,
base64::decode("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=").unwrap()
);
let sha3_256 = hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap();
assert_eq!(sha3_256.hash, base64::decode("XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg=").unwrap());
assert_eq!(
sha3_256.hash,
base64::decode("XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg=").unwrap()
);
}
#[test]
fn test_blake2b_512() {
let hash = hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap();
let known_hash: Vec<u8> = vec!(
0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9,
0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1,
0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52, 0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95,
0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, 0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23,
);
let known_hash: Vec<u8> = vec![
0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12,
0xF6, 0xE9, 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F,
0xDB, 0xFF, 0xA2, 0xD1, 0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52,
0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95, 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A,
0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23,
];
assert_eq!(hash.hash, known_hash);
}
}

View File

@ -24,9 +24,9 @@ impl MessagePayload for ExplicitMessageEncryption {}
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use minidom::Element;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -42,7 +42,9 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<encryption xmlns='urn:xmpp:eme:0' namespace='urn:xmpp:otr:0'/>".parse().unwrap();
let elem: Element = "<encryption xmlns='urn:xmpp:eme:0' namespace='urn:xmpp:otr:0'/>"
.parse()
.unwrap();
let encryption = ExplicitMessageEncryption::try_from(elem).unwrap();
assert_eq!(encryption.namespace, "urn:xmpp:otr:0");
assert_eq!(encryption.name, None);
@ -55,7 +57,9 @@ mod tests {
#[test]
fn test_unknown() {
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>".parse().unwrap();
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
.parse()
.unwrap();
let error = ExplicitMessageEncryption::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -66,7 +70,9 @@ mod tests {
#[test]
fn test_invalid_child() {
let elem: Element = "<encryption xmlns='urn:xmpp:eme:0'><coucou/></encryption>".parse().unwrap();
let elem: Element = "<encryption xmlns='urn:xmpp:eme:0'><coucou/></encryption>"
.parse()
.unwrap();
let error = ExplicitMessageEncryption::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -77,8 +83,13 @@ mod tests {
#[test]
fn test_serialise() {
let elem: Element = "<encryption xmlns='urn:xmpp:eme:0' namespace='coucou'/>".parse().unwrap();
let eme = ExplicitMessageEncryption { namespace: String::from("coucou"), name: None };
let elem: Element = "<encryption xmlns='urn:xmpp:eme:0' namespace='coucou'/>"
.parse()
.unwrap();
let eme = ExplicitMessageEncryption {
namespace: String::from("coucou"),
name: None,
};
let elem2 = eme.into();
assert_eq!(elem, elem2);
}

View File

@ -4,15 +4,14 @@
// 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 base64;
use chrono;
use jid;
use std::convert::From;
use std::num;
use std::string;
use std::fmt;
use std::net;
use base64;
use jid;
use chrono;
use std::num;
use std::string;
/// Contains one of the potential errors triggered while parsing an
/// [Element](../struct.Element.html) into a specialised struct.

View File

@ -26,9 +26,9 @@ generate_element!(
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use minidom::Element;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -50,7 +50,9 @@ mod tests {
#[test]
fn test_invalid_child() {
let elem: Element = "<forwarded xmlns='urn:xmpp:forward:0'><coucou/></forwarded>".parse().unwrap();
let elem: Element = "<forwarded xmlns='urn:xmpp:forward:0'><coucou/></forwarded>"
.parse()
.unwrap();
let error = Forwarded::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -62,7 +64,10 @@ mod tests {
#[test]
fn test_serialise() {
let elem: Element = "<forwarded xmlns='urn:xmpp:forward:0'/>".parse().unwrap();
let forwarded = Forwarded { delay: None, stanza: None };
let forwarded = Forwarded {
delay: None,
stanza: None,
};
let elem2 = forwarded.into();
assert_eq!(elem, elem2);
}

View File

@ -4,14 +4,11 @@
// 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 std::str::FromStr;
use minidom::IntoAttributeValue;
use crate::error::Error;
use crate::helpers::Base64;
use base64;
use minidom::IntoAttributeValue;
use std::str::FromStr;
/// List of the algorithms we support, or Unknown.
#[allow(non_camel_case_types)]
@ -114,10 +111,7 @@ generate_element!(
impl Hash {
/// Creates a [Hash] element with the given algo and data.
pub fn new(algo: Algo, hash: Vec<u8>) -> Hash {
Hash {
algo,
hash,
}
Hash { algo, hash }
}
/// Like [new](#method.new) but takes base64-encoded data before decoding
@ -130,8 +124,8 @@ impl Hash {
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -152,12 +146,17 @@ mod tests {
let elem: Element = "<hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=</hash>".parse().unwrap();
let hash = Hash::try_from(elem).unwrap();
assert_eq!(hash.algo, Algo::Sha_256);
assert_eq!(hash.hash, base64::decode("2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=").unwrap());
assert_eq!(
hash.hash,
base64::decode("2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=").unwrap()
);
}
#[test]
fn test_unknown() {
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>".parse().unwrap();
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
.parse()
.unwrap();
let error = Hash::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -168,7 +167,9 @@ mod tests {
#[test]
fn test_invalid_child() {
let elem: Element = "<hash xmlns='urn:xmpp:hashes:2'><coucou/></hash>".parse().unwrap();
let elem: Element = "<hash xmlns='urn:xmpp:hashes:2'><coucou/></hash>"
.parse()
.unwrap();
let error = Hash::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,

View File

@ -4,8 +4,8 @@
// 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 base64;
use crate::error::Error;
use base64;
/// Codec for plain text content.
pub struct PlainText;
@ -19,9 +19,7 @@ impl PlainText {
}
pub fn encode(string: &Option<String>) -> Option<String> {
string.as_ref().map(|text| {
text.to_owned()
})
string.as_ref().map(|text| text.to_owned())
}
}

View File

@ -8,8 +8,9 @@ use crate::helpers::Base64;
use crate::iq::IqSetPayload;
generate_id!(
/// An identifier matching a stream.
StreamId);
/// An identifier matching a stream.
StreamId
);
generate_attribute!(
/// Which stanza type to use to exchange data.
@ -71,10 +72,10 @@ impl IqSetPayload for Close {}
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use minidom::Element;
use std::error::Error as StdError;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -98,26 +99,36 @@ mod tests {
fn test_simple() {
let sid = StreamId(String::from("coucou"));
let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='3' sid='coucou'/>".parse().unwrap();
let elem: Element =
"<open xmlns='http://jabber.org/protocol/ibb' block-size='3' sid='coucou'/>"
.parse()
.unwrap();
let open = Open::try_from(elem).unwrap();
assert_eq!(open.block_size, 3);
assert_eq!(open.sid, sid);
assert_eq!(open.stanza, Stanza::Iq);
let elem: Element = "<data xmlns='http://jabber.org/protocol/ibb' seq='0' sid='coucou'>AAAA</data>".parse().unwrap();
let elem: Element =
"<data xmlns='http://jabber.org/protocol/ibb' seq='0' sid='coucou'>AAAA</data>"
.parse()
.unwrap();
let data = Data::try_from(elem).unwrap();
assert_eq!(data.seq, 0);
assert_eq!(data.sid, sid);
assert_eq!(data.data, vec!(0, 0, 0));
let elem: Element = "<close xmlns='http://jabber.org/protocol/ibb' sid='coucou'/>".parse().unwrap();
let elem: Element = "<close xmlns='http://jabber.org/protocol/ibb' sid='coucou'/>"
.parse()
.unwrap();
let close = Close::try_from(elem).unwrap();
assert_eq!(close.sid, sid);
}
#[test]
fn test_invalid() {
let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb'/>".parse().unwrap();
let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb'/>"
.parse()
.unwrap();
let error = Open::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -125,7 +136,9 @@ mod tests {
};
assert_eq!(message, "Required attribute 'block-size' missing.");
let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='-5'/>".parse().unwrap();
let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='-5'/>"
.parse()
.unwrap();
let error = Open::try_from(elem).unwrap_err();
let message = match error {
Error::ParseIntError(error) => error,
@ -133,7 +146,9 @@ mod tests {
};
assert_eq!(message.description(), "invalid digit found in string");
let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128'/>".parse().unwrap();
let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128'/>"
.parse()
.unwrap();
let error = Open::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(error) => error,

View File

@ -4,18 +4,14 @@
// 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 crate::data_forms::DataForm;
use crate::error::Error;
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
use crate::ns;
use minidom::Element;
use std::collections::HashMap;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload};
use crate::data_forms::DataForm;
use crate::ns;
/// Query for registering against a service.
#[derive(Debug, Clone)]
pub struct Query {
@ -31,7 +27,6 @@ pub struct Query {
/// A data form the user must fill before being allowed to register.
pub form: Option<DataForm>,
// Not yet implemented.
//pub oob: Option<Oob>,
}
@ -55,9 +50,26 @@ impl TryFrom<Element> for Query {
let namespace = child.ns().unwrap();
if namespace == ns::REGISTER {
let name = child.name();
let fields = vec!["address", "city", "date", "email", "first", "instructions",
"key", "last", "misc", "name", "nick", "password", "phone",
"state", "text", "url", "username", "zip"];
let fields = vec![
"address",
"city",
"date",
"email",
"first",
"instructions",
"key",
"last",
"misc",
"name",
"nick",
"password",
"phone",
"state",
"text",
"url",
"username",
"zip",
];
if fields.binary_search(&name).is_ok() {
query.fields.insert(name.to_owned(), child.text());
} else if name == "registered" {
@ -80,14 +92,26 @@ impl TryFrom<Element> for Query {
impl From<Query> for Element {
fn from(query: Query) -> Element {
Element::builder("query")
.ns(ns::REGISTER)
.append(if query.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None })
.append(query.fields.into_iter().map(|(name, value)| {
Element::builder(name).ns(ns::REGISTER).append(value)
}).collect::<Vec<_>>())
.append(if query.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None })
.append(query.form)
.build()
.ns(ns::REGISTER)
.append(if query.registered {
Some(Element::builder("registered").ns(ns::REGISTER))
} else {
None
})
.append(
query
.fields
.into_iter()
.map(|(name, value)| Element::builder(name).ns(ns::REGISTER).append(value))
.collect::<Vec<_>>(),
)
.append(if query.remove {
Some(Element::builder("remove").ns(ns::REGISTER))
} else {
None
})
.append(query.form)
.build()
}
}
@ -126,7 +150,9 @@ mod tests {
<password/>
<email/>
</query>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let query = Query::try_from(elem).unwrap();
assert_eq!(query.registered, false);
assert_eq!(query.fields["instructions"], "\n Choose a username and password for use with this service.\n Please also provide your email address.\n ");
@ -172,16 +198,27 @@ mod tests {
</field>
</x>
</query>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let elem1 = elem.clone();
let query = Query::try_from(elem).unwrap();
assert_eq!(query.registered, false);
assert!(!query.fields["instructions"].is_empty());
let form = query.form.clone().unwrap();
assert!(!form.instructions.unwrap().is_empty());
assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("first"))).is_ok());
assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("x-gender"))).is_ok());
assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("coucou"))).is_err());
assert!(form
.fields
.binary_search_by(|field| field.var.cmp(&String::from("first")))
.is_ok());
assert!(form
.fields
.binary_search_by(|field| field.var.cmp(&String::from("x-gender")))
.is_ok());
assert!(form
.fields
.binary_search_by(|field| field.var.cmp(&String::from("coucou")))
.is_err());
let elem2 = query.into();
assert!(elem1.compare_to(&elem2));
}
@ -208,7 +245,9 @@ mod tests {
</field>
</x>
</query>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let elem1 = elem.clone();
let query = Query::try_from(elem).unwrap();
assert_eq!(query.registered, false);

View File

@ -4,8 +4,8 @@
// 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 crate::presence::PresencePayload;
use crate::date::DateTime;
use crate::presence::PresencePayload;
generate_element!(
/// Represents the last time the user interacted with their system.
@ -21,11 +21,11 @@ impl PresencePayload for Idle {}
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use std::str::FromStr;
use minidom::Element;
use std::error::Error as StdError;
use std::str::FromStr;
use try_from::TryFrom;
#[test]
fn test_size() {
@ -34,13 +34,17 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-21T20:19:55+01:00'/>".parse().unwrap();
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-21T20:19:55+01:00'/>"
.parse()
.unwrap();
Idle::try_from(elem).unwrap();
}
#[test]
fn test_invalid_child() {
let elem: Element = "<idle xmlns='urn:xmpp:idle:1'><coucou/></idle>".parse().unwrap();
let elem: Element = "<idle xmlns='urn:xmpp:idle:1'><coucou/></idle>"
.parse()
.unwrap();
let error = Idle::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -63,7 +67,9 @@ mod tests {
#[test]
fn test_invalid_date() {
// There is no thirteenth month.
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-13-01T12:23:34Z'/>".parse().unwrap();
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-13-01T12:23:34Z'/>"
.parse()
.unwrap();
let error = Idle::try_from(elem).unwrap_err();
let message = match error {
Error::ChronoParseError(string) => string,
@ -72,7 +78,9 @@ mod tests {
assert_eq!(message.description(), "input is out of range");
// Timezone ≥24:00 arent allowed.
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11:02+25:00'/>".parse().unwrap();
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11:02+25:00'/>"
.parse()
.unwrap();
let error = Idle::try_from(elem).unwrap_err();
let message = match error {
Error::ChronoParseError(string) => string,
@ -81,7 +89,9 @@ mod tests {
assert_eq!(message.description(), "input is out of range");
// Timezone without the : separator arent allowed.
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11:02+0100'/>".parse().unwrap();
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11:02+0100'/>"
.parse()
.unwrap();
let error = Idle::try_from(elem).unwrap_err();
let message = match error {
Error::ChronoParseError(string) => string,
@ -90,7 +100,9 @@ mod tests {
assert_eq!(message.description(), "input contains invalid characters");
// No seconds, error message could be improved.
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11+01:00'/>".parse().unwrap();
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11+01:00'/>"
.parse()
.unwrap();
let error = Idle::try_from(elem).unwrap_err();
let message = match error {
Error::ChronoParseError(string) => string,
@ -99,7 +111,9 @@ mod tests {
assert_eq!(message.description(), "input contains invalid characters");
// TODO: maybe well want to support this one, as per XEP-0082 §4.
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='20170527T12:11:02+01:00'/>".parse().unwrap();
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='20170527T12:11:02+01:00'/>"
.parse()
.unwrap();
let error = Idle::try_from(elem).unwrap_err();
let message = match error {
Error::ChronoParseError(string) => string,
@ -108,7 +122,9 @@ mod tests {
assert_eq!(message.description(), "input contains invalid characters");
// No timezone.
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11:02'/>".parse().unwrap();
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-27T12:11:02'/>"
.parse()
.unwrap();
let error = Idle::try_from(elem).unwrap_err();
let message = match error {
Error::ChronoParseError(string) => string,
@ -119,8 +135,12 @@ mod tests {
#[test]
fn test_serialise() {
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-21T20:19:55+01:00'/>".parse().unwrap();
let idle = Idle { since: DateTime::from_str("2017-05-21T20:19:55+01:00").unwrap() };
let elem: Element = "<idle xmlns='urn:xmpp:idle:1' since='2017-05-21T20:19:55+01:00'/>"
.parse()
.unwrap();
let idle = Idle {
since: DateTime::from_str("2017-05-21T20:19:55+01:00").unwrap(),
};
let elem2 = idle.into();
assert_eq!(elem, elem2);
}

111
src/iq.rs
View File

@ -5,18 +5,13 @@
// 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 try_from::TryFrom;
use crate::error::Error;
use crate::ns;
use crate::stanza_error::StanzaError;
use jid::Jid;
use minidom::Element;
use minidom::IntoAttributeValue;
use jid::Jid;
use crate::error::Error;
use crate::ns;
use crate::stanza_error::StanzaError;
use try_from::TryFrom;
/// Should be implemented on every known payload of an `<iq type='get'/>`.
pub trait IqGetPayload: TryFrom<Element> + Into<Element> {}
@ -45,12 +40,15 @@ pub enum IqType {
impl<'a> IntoAttributeValue for &'a IqType {
fn into_attribute_value(self) -> Option<String> {
Some(match *self {
IqType::Get(_) => "get",
IqType::Set(_) => "set",
IqType::Result(_) => "result",
IqType::Error(_) => "error",
}.to_owned())
Some(
match *self {
IqType::Get(_) => "get",
IqType::Set(_) => "set",
IqType::Result(_) => "result",
IqType::Error(_) => "error",
}
.to_owned(),
)
}
}
@ -201,16 +199,14 @@ impl TryFrom<Element> for Iq {
impl From<Iq> for Element {
fn from(iq: Iq) -> Element {
let mut stanza = Element::builder("iq")
.ns(ns::DEFAULT_NS)
.attr("from", iq.from)
.attr("to", iq.to)
.attr("id", iq.id)
.attr("type", &iq.payload)
.build();
.ns(ns::DEFAULT_NS)
.attr("from", iq.from)
.attr("to", iq.to)
.attr("id", iq.id)
.attr("type", &iq.payload)
.build();
let elem = match iq.payload {
IqType::Get(elem)
| IqType::Set(elem)
| IqType::Result(Some(elem)) => elem,
IqType::Get(elem) | IqType::Set(elem) | IqType::Result(Some(elem)) => elem,
IqType::Error(error) => error.into(),
IqType::Result(None) => return stanza,
};
@ -222,9 +218,9 @@ impl From<Iq> for Element {
#[cfg(test)]
mod tests {
use super::*;
use crate::stanza_error::{ErrorType, DefinedCondition};
use crate::compare_elements::NamespaceAwareCompare;
use crate::disco::DiscoInfoQuery;
use crate::stanza_error::{DefinedCondition, ErrorType};
#[cfg(target_pointer_width = "32")]
#[test]
@ -259,11 +255,15 @@ mod tests {
#[cfg(not(feature = "component"))]
let elem: Element = "<iq xmlns='jabber:client' type='get'>
<foo xmlns='bar'/>
</iq>".parse().unwrap();
</iq>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<iq xmlns='jabber:component:accept' type='get'>
<foo xmlns='bar'/>
</iq>".parse().unwrap();
</iq>"
.parse()
.unwrap();
let iq = Iq::try_from(elem).unwrap();
let query: Element = "<foo xmlns='bar'/>".parse().unwrap();
assert_eq!(iq.from, None);
@ -271,7 +271,7 @@ mod tests {
assert_eq!(iq.id, None);
assert!(match iq.payload {
IqType::Get(element) => element.compare_to(&query),
_ => false
_ => false,
});
}
@ -280,11 +280,15 @@ mod tests {
#[cfg(not(feature = "component"))]
let elem: Element = "<iq xmlns='jabber:client' type='set'>
<vCard xmlns='vcard-temp'/>
</iq>".parse().unwrap();
</iq>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<iq xmlns='jabber:component:accept' type='set'>
<vCard xmlns='vcard-temp'/>
</iq>".parse().unwrap();
</iq>"
.parse()
.unwrap();
let iq = Iq::try_from(elem).unwrap();
let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
assert_eq!(iq.from, None);
@ -292,7 +296,7 @@ mod tests {
assert_eq!(iq.id, None);
assert!(match iq.payload {
IqType::Set(element) => element.compare_to(&vcard),
_ => false
_ => false,
});
}
@ -301,7 +305,9 @@ mod tests {
#[cfg(not(feature = "component"))]
let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
#[cfg(feature = "component")]
let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>".parse().unwrap();
let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>"
.parse()
.unwrap();
let iq = Iq::try_from(elem).unwrap();
assert_eq!(iq.from, None);
assert_eq!(iq.to, None);
@ -317,13 +323,19 @@ mod tests {
#[cfg(not(feature = "component"))]
let elem: Element = "<iq xmlns='jabber:client' type='result'>
<query xmlns='http://jabber.org/protocol/disco#items'/>
</iq>".parse().unwrap();
</iq>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<iq xmlns='jabber:component:accept' type='result'>
<query xmlns='http://jabber.org/protocol/disco#items'/>
</iq>".parse().unwrap();
</iq>"
.parse()
.unwrap();
let iq = Iq::try_from(elem).unwrap();
let query: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>".parse().unwrap();
let query: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>"
.parse()
.unwrap();
assert_eq!(iq.from, None);
assert_eq!(iq.to, None);
assert_eq!(iq.id, None);
@ -341,14 +353,18 @@ mod tests {
<error type='cancel'>
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>".parse().unwrap();
</iq>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<iq xmlns='jabber:component:accept' type='error'>
<ping xmlns='urn:xmpp:ping'/>
<error type='cancel'>
<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</iq>".parse().unwrap();
</iq>"
.parse()
.unwrap();
let iq = Iq::try_from(elem).unwrap();
assert_eq!(iq.from, None);
assert_eq!(iq.to, None);
@ -357,10 +373,13 @@ mod tests {
IqType::Error(error) => {
assert_eq!(error.type_, ErrorType::Cancel);
assert_eq!(error.by, None);
assert_eq!(error.defined_condition, DefinedCondition::ServiceUnavailable);
assert_eq!(
error.defined_condition,
DefinedCondition::ServiceUnavailable
);
assert_eq!(error.texts.len(), 0);
assert_eq!(error.other, None);
},
}
_ => panic!(),
}
}
@ -368,9 +387,13 @@ mod tests {
#[test]
fn test_children_invalid() {
#[cfg(not(feature = "component"))]
let elem: Element = "<iq xmlns='jabber:client' type='error'></iq>".parse().unwrap();
let elem: Element = "<iq xmlns='jabber:client' type='error'></iq>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<iq xmlns='jabber:component:accept' type='error'></iq>".parse().unwrap();
let elem: Element = "<iq xmlns='jabber:component:accept' type='error'></iq>"
.parse()
.unwrap();
let error = Iq::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -384,7 +407,9 @@ mod tests {
#[cfg(not(feature = "component"))]
let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
#[cfg(feature = "component")]
let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>".parse().unwrap();
let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>"
.parse()
.unwrap();
let iq2 = Iq {
from: None,
to: None,

View File

@ -4,15 +4,13 @@
// 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 try_from::TryFrom;
use std::str::FromStr;
use minidom::Element;
use jid::Jid;
use crate::error::Error;
use crate::ns;
use crate::iq::IqSetPayload;
use crate::ns;
use jid::Jid;
use minidom::Element;
use std::str::FromStr;
use try_from::TryFrom;
generate_attribute!(
/// The action attribute.
@ -351,7 +349,8 @@ impl From<Reason> for Element {
Reason::Timeout => "timeout",
Reason::UnsupportedApplications => "unsupported-applications",
Reason::UnsupportedTransports => "unsupported-transports",
}).build()
})
.build()
}
}
@ -379,19 +378,25 @@ impl TryFrom<Element> for ReasonElement {
match child.name() {
"text" => {
if text.is_some() {
return Err(Error::ParseError("Reason must not have more than one text."));
return Err(Error::ParseError(
"Reason must not have more than one text.",
));
}
text = Some(child.text());
},
}
name => {
if reason.is_some() {
return Err(Error::ParseError("Reason must not have more than one reason."));
return Err(Error::ParseError(
"Reason must not have more than one reason.",
));
}
reason = Some(name.parse()?);
},
}
}
}
let reason = reason.ok_or(Error::ParseError("Reason doesnt contain a valid reason."))?;
let reason = reason.ok_or(Error::ParseError(
"Reason doesnt contain a valid reason.",
))?;
Ok(ReasonElement {
reason: reason,
text: text,
@ -402,9 +407,9 @@ impl TryFrom<Element> for ReasonElement {
impl From<ReasonElement> for Element {
fn from(reason: ReasonElement) -> Element {
Element::builder("reason")
.append(Element::from(reason.reason))
.append(reason.text)
.build()
.append(Element::from(reason.reason))
.append(reason.text)
.build()
}
}
@ -491,9 +496,9 @@ impl TryFrom<Element> for Jingle {
initiator: get_attr!(root, "initiator", optional),
responder: get_attr!(root, "responder", optional),
sid: get_attr!(root, "sid", required),
contents: vec!(),
contents: vec![],
reason: None,
other: vec!(),
other: vec![],
};
for child in root.children().cloned() {
@ -502,7 +507,9 @@ impl TryFrom<Element> for Jingle {
jingle.contents.push(content);
} else if child.is("reason", ns::JINGLE) {
if jingle.reason.is_some() {
return Err(Error::ParseError("Jingle must not have more than one reason."));
return Err(Error::ParseError(
"Jingle must not have more than one reason.",
));
}
let reason = ReasonElement::try_from(child)?;
jingle.reason = Some(reason);
@ -518,14 +525,14 @@ impl TryFrom<Element> for Jingle {
impl From<Jingle> for Element {
fn from(jingle: Jingle) -> Element {
Element::builder("jingle")
.ns(ns::JINGLE)
.attr("action", jingle.action)
.attr("initiator", jingle.initiator)
.attr("responder", jingle.responder)
.attr("sid", jingle.sid)
.append(jingle.contents)
.append(jingle.reason)
.build()
.ns(ns::JINGLE)
.attr("action", jingle.action)
.attr("initiator", jingle.initiator)
.attr("responder", jingle.responder)
.attr("sid", jingle.sid)
.append(jingle.contents)
.append(jingle.reason)
.build()
}
}
@ -565,7 +572,10 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'/>".parse().unwrap();
let elem: Element =
"<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'/>"
.parse()
.unwrap();
let jingle = Jingle::try_from(elem).unwrap();
assert_eq!(jingle.action, Action::SessionInitiate);
assert_eq!(jingle.sid, SessionId(String::from("coucou")));
@ -581,7 +591,9 @@ mod tests {
};
assert_eq!(message, "Required attribute 'action' missing.");
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-info'/>".parse().unwrap();
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-info'/>"
.parse()
.unwrap();
let error = Jingle::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -589,7 +601,9 @@ mod tests {
};
assert_eq!(message, "Required attribute 'sid' missing.");
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='coucou' sid='coucou'/>".parse().unwrap();
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='coucou' sid='coucou'/>"
.parse()
.unwrap();
let error = Jingle::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,

View File

@ -4,19 +4,15 @@
// 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 try_from::TryFrom;
use std::str::FromStr;
use std::collections::BTreeMap;
use crate::hashes::Hash;
use crate::jingle::{Creator, ContentId};
use crate::date::DateTime;
use minidom::Element;
use crate::error::Error;
use crate::hashes::Hash;
use crate::jingle::{ContentId, Creator};
use crate::ns;
use minidom::Element;
use std::collections::BTreeMap;
use std::str::FromStr;
use try_from::TryFrom;
generate_element!(
/// Represents a range in a file.
@ -153,7 +149,7 @@ impl TryFrom<Element> for File {
descs: BTreeMap::new(),
size: None,
range: None,
hashes: vec!(),
hashes: vec![],
};
for child in elem.children() {
@ -164,7 +160,9 @@ impl TryFrom<Element> for File {
file.date = Some(child.text().parse()?);
} else if child.is("media-type", ns::JINGLE_FT) {
if file.media_type.is_some() {
return Err(Error::ParseError("File must not have more than one media-type."));
return Err(Error::ParseError(
"File must not have more than one media-type.",
));
}
file.media_type = Some(child.text());
} else if child.is("name", ns::JINGLE_FT) {
@ -176,7 +174,9 @@ impl TryFrom<Element> for File {
let lang = get_attr!(child, "xml:lang", default);
let desc = Desc(child.text());
if file.descs.insert(lang, desc).is_some() {
return Err(Error::ParseError("Desc element present twice for the same xml:lang."));
return Err(Error::ParseError(
"Desc element present twice for the same xml:lang.",
));
}
} else if child.is("size", ns::JINGLE_FT) {
if file.size.is_some() {
@ -201,38 +201,47 @@ impl TryFrom<Element> for File {
impl From<File> for Element {
fn from(file: File) -> Element {
let mut root = Element::builder("file")
.ns(ns::JINGLE_FT);
let mut root = Element::builder("file").ns(ns::JINGLE_FT);
if let Some(date) = file.date {
root = root.append(Element::builder("date")
.ns(ns::JINGLE_FT)
.append(date)
.build());
root = root.append(
Element::builder("date")
.ns(ns::JINGLE_FT)
.append(date)
.build(),
);
}
if let Some(media_type) = file.media_type {
root = root.append(Element::builder("media-type")
.ns(ns::JINGLE_FT)
.append(media_type)
.build());
root = root.append(
Element::builder("media-type")
.ns(ns::JINGLE_FT)
.append(media_type)
.build(),
);
}
if let Some(name) = file.name {
root = root.append(Element::builder("name")
.ns(ns::JINGLE_FT)
.append(name)
.build());
root = root.append(
Element::builder("name")
.ns(ns::JINGLE_FT)
.append(name)
.build(),
);
}
for (lang, desc) in file.descs.into_iter() {
root = root.append(Element::builder("desc")
.ns(ns::JINGLE_FT)
.attr("xml:lang", lang)
.append(desc.0)
.build());
root = root.append(
Element::builder("desc")
.ns(ns::JINGLE_FT)
.attr("xml:lang", lang)
.append(desc.0)
.build(),
);
}
if let Some(size) = file.size {
root = root.append(Element::builder("size")
.ns(ns::JINGLE_FT)
.append(format!("{}", size))
.build());
root = root.append(
Element::builder("size")
.ns(ns::JINGLE_FT)
.append(format!("{}", size))
.build(),
);
}
if let Some(range) = file.range {
root = root.append(range);
@ -260,12 +269,16 @@ impl TryFrom<Element> for Description {
let mut file = None;
for child in elem.children() {
if file.is_some() {
return Err(Error::ParseError("JingleFT description element must have exactly one child."));
return Err(Error::ParseError(
"JingleFT description element must have exactly one child.",
));
}
file = Some(File::try_from(child.clone())?);
}
if file.is_none() {
return Err(Error::ParseError("JingleFT description element must have exactly one child."));
return Err(Error::ParseError(
"JingleFT description element must have exactly one child.",
));
}
Ok(Description {
file: file.unwrap(),
@ -276,9 +289,9 @@ impl TryFrom<Element> for Description {
impl From<Description> for Element {
fn from(description: Description) -> Element {
Element::builder("description")
.ns(ns::JINGLE_FT)
.append(description.file)
.build()
.ns(ns::JINGLE_FT)
.append(description.file)
.build()
}
}
@ -304,12 +317,16 @@ impl TryFrom<Element> for Checksum {
let mut file = None;
for child in elem.children() {
if file.is_some() {
return Err(Error::ParseError("JingleFT checksum element must have exactly one child."));
return Err(Error::ParseError(
"JingleFT checksum element must have exactly one child.",
));
}
file = Some(File::try_from(child.clone())?);
}
if file.is_none() {
return Err(Error::ParseError("JingleFT checksum element must have exactly one child."));
return Err(Error::ParseError(
"JingleFT checksum element must have exactly one child.",
));
}
Ok(Checksum {
name: get_attr!(elem, "name", required),
@ -322,11 +339,11 @@ impl TryFrom<Element> for Checksum {
impl From<Checksum> for Element {
fn from(checksum: Checksum) -> Element {
Element::builder("checksum")
.ns(ns::JINGLE_FT)
.attr("name", checksum.name)
.attr("creator", checksum.creator)
.append(checksum.file)
.build()
.ns(ns::JINGLE_FT)
.attr("name", checksum.name)
.attr("creator", checksum.creator)
.append(checksum.file)
.build()
}
}
@ -381,16 +398,24 @@ mod tests {
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
</file>
</description>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let desc = Description::try_from(elem).unwrap();
assert_eq!(desc.file.media_type, Some(String::from("text/plain")));
assert_eq!(desc.file.name, Some(String::from("test.txt")));
assert_eq!(desc.file.descs, BTreeMap::new());
assert_eq!(desc.file.date, DateTime::from_str("2015-07-26T21:46:00+01:00").ok());
assert_eq!(
desc.file.date,
DateTime::from_str("2015-07-26T21:46:00+01:00").ok()
);
assert_eq!(desc.file.size, Some(6144u64));
assert_eq!(desc.file.range, None);
assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1);
assert_eq!(desc.file.hashes[0].hash, base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap());
assert_eq!(
desc.file.hashes[0].hash,
base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap()
);
}
#[test]
@ -402,7 +427,9 @@ mod tests {
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
</file>
</description>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let desc = Description::try_from(elem).unwrap();
assert_eq!(desc.file.media_type, None);
assert_eq!(desc.file.name, None);
@ -411,7 +438,10 @@ mod tests {
assert_eq!(desc.file.size, None);
assert_eq!(desc.file.range, None);
assert_eq!(desc.file.hashes[0].algo, Algo::Sha_1);
assert_eq!(desc.file.hashes[0].hash, base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap());
assert_eq!(
desc.file.hashes[0].hash,
base64::decode("w0mcJylzCn+AfvuGdqkty2+KP48=").unwrap()
);
}
#[test]
@ -426,11 +456,19 @@ mod tests {
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
</file>
</description>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let desc = Description::try_from(elem).unwrap();
assert_eq!(desc.file.descs.keys().cloned().collect::<Vec<_>>(), ["en", "fr"]);
assert_eq!(
desc.file.descs.keys().cloned().collect::<Vec<_>>(),
["en", "fr"]
);
assert_eq!(desc.file.descs["en"], Desc(String::from("Secret file!")));
assert_eq!(desc.file.descs["fr"], Desc(String::from("Fichier secret!")));
assert_eq!(
desc.file.descs["fr"],
Desc(String::from("Fichier secret!"))
);
let elem: Element = r#"
<description xmlns='urn:xmpp:jingle:apps:file-transfer:5'>
@ -442,7 +480,9 @@ mod tests {
algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>
</file>
</description>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let error = Description::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -478,7 +518,10 @@ mod tests {
};
assert_eq!(message, "Unknown attribute in received element.");
let elem: Element = "<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' creator='initiator'/>".parse().unwrap();
let elem: Element =
"<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' creator='initiator'/>"
.parse()
.unwrap();
let error = Received::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -498,16 +541,31 @@ mod tests {
#[test]
fn test_checksum() {
let elem: Element = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator'><file><hash xmlns='urn:xmpp:hashes:2' algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash></file></checksum>".parse().unwrap();
let hash = vec!(195, 73, 156, 39, 41, 115, 10, 127, 128, 126, 251, 134, 118, 169, 45, 203, 111, 138, 63, 143);
let hash = vec![
195, 73, 156, 39, 41, 115, 10, 127, 128, 126, 251, 134, 118, 169, 45, 203, 111, 138,
63, 143,
];
let checksum = Checksum::try_from(elem).unwrap();
assert_eq!(checksum.name, ContentId(String::from("coucou")));
assert_eq!(checksum.creator, Creator::Initiator);
assert_eq!(checksum.file.hashes, vec!(Hash { algo: Algo::Sha_1, hash: hash.clone() }));
assert_eq!(
checksum.file.hashes,
vec!(Hash {
algo: Algo::Sha_1,
hash: hash.clone()
})
);
let elem2 = Element::from(checksum);
let checksum2 = Checksum::try_from(elem2).unwrap();
assert_eq!(checksum2.name, ContentId(String::from("coucou")));
assert_eq!(checksum2.creator, Creator::Initiator);
assert_eq!(checksum2.file.hashes, vec!(Hash { algo: Algo::Sha_1, hash: hash.clone() }));
assert_eq!(
checksum2.file.hashes,
vec!(Hash {
algo: Algo::Sha_1,
hash: hash.clone()
})
);
let elem: Element = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator'><coucou/></checksum>".parse().unwrap();
let error = Checksum::try_from(elem).unwrap_err();
@ -544,14 +602,22 @@ mod tests {
#[test]
fn test_range() {
let elem: Element = "<range xmlns='urn:xmpp:jingle:apps:file-transfer:5'/>".parse().unwrap();
let elem: Element = "<range xmlns='urn:xmpp:jingle:apps:file-transfer:5'/>"
.parse()
.unwrap();
let range = Range::try_from(elem).unwrap();
assert_eq!(range.offset, 0);
assert_eq!(range.length, None);
assert_eq!(range.hashes, vec!());
let elem: Element = "<range xmlns='urn:xmpp:jingle:apps:file-transfer:5' offset='2048' length='1024'><hash xmlns='urn:xmpp:hashes:2' algo='sha-1'>kHp5RSzW/h7Gm1etSf90Mr5PC/k=</hash></range>".parse().unwrap();
let hashes = vec!(Hash { algo: Algo::Sha_1, hash: vec!(144, 122, 121, 69, 44, 214, 254, 30, 198, 155, 87, 173, 73, 255, 116, 50, 190, 79, 11, 249) });
let hashes = vec![Hash {
algo: Algo::Sha_1,
hash: vec![
144, 122, 121, 69, 44, 214, 254, 30, 198, 155, 87, 173, 73, 255, 116, 50, 190, 79,
11, 249,
],
}];
let range = Range::try_from(elem).unwrap();
assert_eq!(range.offset, 2048);
assert_eq!(range.length, Some(1024));
@ -562,7 +628,9 @@ mod tests {
assert_eq!(range2.length, Some(1024));
assert_eq!(range2.hashes, hashes);
let elem: Element = "<range xmlns='urn:xmpp:jingle:apps:file-transfer:5' coucou=''/>".parse().unwrap();
let elem: Element = "<range xmlns='urn:xmpp:jingle:apps:file-transfer:5' coucou=''/>"
.parse()
.unwrap();
let error = Range::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,

View File

@ -24,10 +24,10 @@ attributes: [
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use minidom::Element;
use std::error::Error as StdError;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -43,7 +43,10 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='3' sid='coucou'/>".parse().unwrap();
let elem: Element =
"<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='3' sid='coucou'/>"
.parse()
.unwrap();
let transport = Transport::try_from(elem).unwrap();
assert_eq!(transport.block_size, 3);
assert_eq!(transport.sid, StreamId(String::from("coucou")));
@ -52,7 +55,9 @@ mod tests {
#[test]
fn test_invalid() {
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1'/>".parse().unwrap();
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1'/>"
.parse()
.unwrap();
let error = Transport::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -60,15 +65,23 @@ mod tests {
};
assert_eq!(message, "Required attribute 'block-size' missing.");
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='65536'/>".parse().unwrap();
let elem: Element =
"<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='65536'/>"
.parse()
.unwrap();
let error = Transport::try_from(elem).unwrap_err();
let message = match error {
Error::ParseIntError(error) => error,
_ => panic!(),
};
assert_eq!(message.description(), "number too large to fit in target type");
assert_eq!(
message.description(),
"number too large to fit in target type"
);
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='-5'/>".parse().unwrap();
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='-5'/>"
.parse()
.unwrap();
let error = Transport::try_from(elem).unwrap_err();
let message = match error {
Error::ParseIntError(error) => error,
@ -76,7 +89,10 @@ mod tests {
};
assert_eq!(message.description(), "invalid digit found in string");
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='128'/>".parse().unwrap();
let elem: Element =
"<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='128'/>"
.parse()
.unwrap();
let error = Transport::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,

View File

@ -4,15 +4,11 @@
// 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 try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use crate::jingle::SessionId;
use crate::ns;
use minidom::Element;
use try_from::TryFrom;
/// Defines a protocol for broadcasting Jingle requests to all of the clients
/// of a user.
@ -72,9 +68,11 @@ impl TryFrom<Element> for JingleMI {
}
JingleMI::Propose {
sid: get_sid(elem)?,
description: description.ok_or(Error::ParseError("Propose element doesnt contain a description."))?,
description: description.ok_or(Error::ParseError(
"Propose element doesnt contain a description.",
))?,
}
},
}
"retract" => JingleMI::Retract(check_empty_and_get_sid(elem)?),
"accept" => JingleMI::Accept(check_empty_and_get_sid(elem)?),
"proceed" => JingleMI::Proceed(check_empty_and_get_sid(elem)?),
@ -87,33 +85,24 @@ impl TryFrom<Element> for JingleMI {
impl From<JingleMI> for Element {
fn from(jingle_mi: JingleMI) -> Element {
match jingle_mi {
JingleMI::Propose { sid, description } => {
Element::builder("propose")
.ns(ns::JINGLE_MESSAGE)
.attr("id", sid)
.append(description)
},
JingleMI::Retract(sid) => {
Element::builder("retract")
.ns(ns::JINGLE_MESSAGE)
.attr("id", sid)
}
JingleMI::Accept(sid) => {
Element::builder("accept")
.ns(ns::JINGLE_MESSAGE)
.attr("id", sid)
}
JingleMI::Proceed(sid) => {
Element::builder("proceed")
.ns(ns::JINGLE_MESSAGE)
.attr("id", sid)
}
JingleMI::Reject(sid) => {
Element::builder("reject")
.ns(ns::JINGLE_MESSAGE)
.attr("id", sid)
}
}.build()
JingleMI::Propose { sid, description } => Element::builder("propose")
.ns(ns::JINGLE_MESSAGE)
.attr("id", sid)
.append(description),
JingleMI::Retract(sid) => Element::builder("retract")
.ns(ns::JINGLE_MESSAGE)
.attr("id", sid),
JingleMI::Accept(sid) => Element::builder("accept")
.ns(ns::JINGLE_MESSAGE)
.attr("id", sid),
JingleMI::Proceed(sid) => Element::builder("proceed")
.ns(ns::JINGLE_MESSAGE)
.attr("id", sid),
JingleMI::Reject(sid) => Element::builder("reject")
.ns(ns::JINGLE_MESSAGE)
.attr("id", sid),
}
.build()
}
}
@ -135,13 +124,18 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<accept xmlns='urn:xmpp:jingle-message:0' id='coucou'/>".parse().unwrap();
let elem: Element = "<accept xmlns='urn:xmpp:jingle-message:0' id='coucou'/>"
.parse()
.unwrap();
JingleMI::try_from(elem).unwrap();
}
#[test]
fn test_invalid_child() {
let elem: Element = "<propose xmlns='urn:xmpp:jingle-message:0' id='coucou'><coucou/></propose>".parse().unwrap();
let elem: Element =
"<propose xmlns='urn:xmpp:jingle-message:0' id='coucou'><coucou/></propose>"
.parse()
.unwrap();
let error = JingleMI::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,

View File

@ -4,15 +4,12 @@
// 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 try_from::TryFrom;
use std::net::IpAddr;
use minidom::Element;
use jid::Jid;
use crate::error::Error;
use crate::ns;
use jid::Jid;
use minidom::Element;
use std::net::IpAddr;
use try_from::TryFrom;
generate_attribute!(
/// The type of the connection being proposed by this candidate.
@ -187,37 +184,50 @@ impl TryFrom<Element> for Transport {
let mut payload = None;
for child in elem.children() {
payload = Some(if child.is("candidate", ns::JINGLE_S5B) {
let mut candidates = match payload {
Some(TransportPayload::Candidates(candidates)) => candidates,
Some(_) => return Err(Error::ParseError("Non-candidate child already present in JingleS5B transport element.")),
None => vec!(),
};
let mut candidates =
match payload {
Some(TransportPayload::Candidates(candidates)) => candidates,
Some(_) => return Err(Error::ParseError(
"Non-candidate child already present in JingleS5B transport element.",
)),
None => vec![],
};
candidates.push(Candidate::try_from(child.clone())?);
TransportPayload::Candidates(candidates)
} else if child.is("activated", ns::JINGLE_S5B) {
if payload.is_some() {
return Err(Error::ParseError("Non-activated child already present in JingleS5B transport element."));
return Err(Error::ParseError(
"Non-activated child already present in JingleS5B transport element.",
));
}
let cid = get_attr!(child, "cid", required);
TransportPayload::Activated(cid)
} else if child.is("candidate-error", ns::JINGLE_S5B) {
if payload.is_some() {
return Err(Error::ParseError("Non-candidate-error child already present in JingleS5B transport element."));
return Err(Error::ParseError(
"Non-candidate-error child already present in JingleS5B transport element.",
));
}
TransportPayload::CandidateError
} else if child.is("candidate-used", ns::JINGLE_S5B) {
if payload.is_some() {
return Err(Error::ParseError("Non-candidate-used child already present in JingleS5B transport element."));
return Err(Error::ParseError(
"Non-candidate-used child already present in JingleS5B transport element.",
));
}
let cid = get_attr!(child, "cid", required);
TransportPayload::CandidateUsed(cid)
} else if child.is("proxy-error", ns::JINGLE_S5B) {
if payload.is_some() {
return Err(Error::ParseError("Non-proxy-error child already present in JingleS5B transport element."));
return Err(Error::ParseError(
"Non-proxy-error child already present in JingleS5B transport element.",
));
}
TransportPayload::ProxyError
} else {
return Err(Error::ParseError("Unknown child in JingleS5B transport element."));
return Err(Error::ParseError(
"Unknown child in JingleS5B transport element.",
));
});
}
let payload = payload.unwrap_or(TransportPayload::None);
@ -233,49 +243,40 @@ impl TryFrom<Element> for Transport {
impl From<Transport> for Element {
fn from(transport: Transport) -> Element {
Element::builder("transport")
.ns(ns::JINGLE_S5B)
.attr("sid", transport.sid)
.attr("dstaddr", transport.dstaddr)
.attr("mode", transport.mode)
.append(match transport.payload {
TransportPayload::Candidates(candidates) => {
candidates.into_iter()
.map(Element::from)
.collect::<Vec<_>>()
},
TransportPayload::Activated(cid) => {
vec!(Element::builder("activated")
.ns(ns::JINGLE_S5B)
.attr("cid", cid)
.build())
},
TransportPayload::CandidateError => {
vec!(Element::builder("candidate-error")
.ns(ns::JINGLE_S5B)
.build())
},
TransportPayload::CandidateUsed(cid) => {
vec!(Element::builder("candidate-used")
.ns(ns::JINGLE_S5B)
.attr("cid", cid)
.build())
},
TransportPayload::ProxyError => {
vec!(Element::builder("proxy-error")
.ns(ns::JINGLE_S5B)
.build())
},
TransportPayload::None => vec!(),
})
.build()
.ns(ns::JINGLE_S5B)
.attr("sid", transport.sid)
.attr("dstaddr", transport.dstaddr)
.attr("mode", transport.mode)
.append(match transport.payload {
TransportPayload::Candidates(candidates) => candidates
.into_iter()
.map(Element::from)
.collect::<Vec<_>>(),
TransportPayload::Activated(cid) => vec![Element::builder("activated")
.ns(ns::JINGLE_S5B)
.attr("cid", cid)
.build()],
TransportPayload::CandidateError => vec![Element::builder("candidate-error")
.ns(ns::JINGLE_S5B)
.build()],
TransportPayload::CandidateUsed(cid) => vec![Element::builder("candidate-used")
.ns(ns::JINGLE_S5B)
.attr("cid", cid)
.build()],
TransportPayload::ProxyError => {
vec![Element::builder("proxy-error").ns(ns::JINGLE_S5B).build()]
}
TransportPayload::None => vec![],
})
.build()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
use crate::compare_elements::NamespaceAwareCompare;
use std::str::FromStr;
#[cfg(target_pointer_width = "32")]
#[test]
@ -303,7 +304,9 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:s5b:1' sid='coucou'/>".parse().unwrap();
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:s5b:1' sid='coucou'/>"
.parse()
.unwrap();
let transport = Transport::try_from(elem).unwrap();
assert_eq!(transport.sid, StreamId(String::from("coucou")));
assert_eq!(transport.dstaddr, None);
@ -334,14 +337,14 @@ mod tests {
sid: StreamId(String::from("coucou")),
dstaddr: None,
mode: Mode::Tcp,
payload: TransportPayload::Candidates(vec!(Candidate {
payload: TransportPayload::Candidates(vec![Candidate {
cid: CandidateId(String::from("coucou")),
host: IpAddr::from_str("127.0.0.1").unwrap(),
jid: Jid::from_str("coucou@coucou").unwrap(),
port: None,
priority: 0u32,
type_: Type::Direct,
})),
}]),
};
let elem2: Element = transport.into();
assert!(elem.compare_to(&elem2));

View File

@ -24,15 +24,15 @@
#![deny(missing_docs)]
extern crate minidom;
extern crate jid;
extern crate base64;
extern crate blake2;
extern crate chrono;
extern crate digest;
extern crate jid;
extern crate minidom;
extern crate sha1;
extern crate sha2;
extern crate sha3;
extern crate blake2;
extern crate chrono;
extern crate try_from;
pub use minidom::Element;
@ -52,20 +52,20 @@ mod macros;
/// Namespace-aware comparison for tests
mod compare_elements;
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
pub mod bind;
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
pub mod iq;
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
pub mod message;
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
pub mod presence;
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
pub mod iq;
pub mod sasl;
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
pub mod stanza_error;
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
pub mod stream;
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
pub mod sasl;
/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core
pub mod bind;
/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence
pub mod roster;

View File

@ -5,34 +5,40 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
macro_rules! get_attr {
($elem:ident, $attr:tt, $type:tt) => (
($elem:ident, $attr:tt, $type:tt) => {
get_attr!($elem, $attr, $type, value, value.parse()?)
);
($elem:ident, $attr:tt, optional_empty, $value:ident, $func:expr) => (
};
($elem:ident, $attr:tt, optional_empty, $value:ident, $func:expr) => {
match $elem.attr($attr) {
Some("") => None,
Some($value) => Some($func),
None => None,
}
);
($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => (
};
($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => {
match $elem.attr($attr) {
Some($value) => Some($func),
None => None,
}
);
($elem:ident, $attr:tt, required, $value:ident, $func:expr) => (
};
($elem:ident, $attr:tt, required, $value:ident, $func:expr) => {
match $elem.attr($attr) {
Some($value) => $func,
None => return Err(crate::error::Error::ParseError(concat!("Required attribute '", $attr, "' missing."))),
None => {
return Err(crate::error::Error::ParseError(concat!(
"Required attribute '",
$attr,
"' missing."
)))
}
}
);
($elem:ident, $attr:tt, default, $value:ident, $func:expr) => (
};
($elem:ident, $attr:tt, default, $value:ident, $func:expr) => {
match $elem.attr($attr) {
Some($value) => $func,
None => ::std::default::Default::default(),
}
);
};
}
macro_rules! generate_attribute {
@ -211,38 +217,54 @@ macro_rules! generate_attribute_enum {
}
macro_rules! check_self {
($elem:ident, $name:tt, $ns:ident) => (
($elem:ident, $name:tt, $ns:ident) => {
check_self!($elem, $name, $ns, $name);
);
($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => (
};
($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => {
if !$elem.is($name, crate::ns::$ns) {
return Err(crate::error::Error::ParseError(concat!("This is not a ", $pretty_name, " element.")));
return Err(crate::error::Error::ParseError(concat!(
"This is not a ",
$pretty_name,
" element."
)));
}
);
};
}
macro_rules! check_ns_only {
($elem:ident, $name:tt, $ns:ident) => (
($elem:ident, $name:tt, $ns:ident) => {
if !$elem.has_ns(crate::ns::$ns) {
return Err(crate::error::Error::ParseError(concat!("This is not a ", $name, " element.")));
return Err(crate::error::Error::ParseError(concat!(
"This is not a ",
$name,
" element."
)));
}
);
};
}
macro_rules! check_no_children {
($elem:ident, $name:tt) => (
($elem:ident, $name:tt) => {
for _ in $elem.children() {
return Err(crate::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
return Err(crate::error::Error::ParseError(concat!(
"Unknown child in ",
$name,
" element."
)));
}
);
};
}
macro_rules! check_no_attributes {
($elem:ident, $name:tt) => (
($elem:ident, $name:tt) => {
for _ in $elem.attrs() {
return Err(crate::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element.")));
return Err(crate::error::Error::ParseError(concat!(
"Unknown attribute in ",
$name,
" element."
)));
}
);
};
}
macro_rules! check_no_unknown_attributes {
@ -351,76 +373,95 @@ macro_rules! start_decl {
}
macro_rules! start_parse_elem {
($temp:ident: Vec) => (
($temp:ident: Vec) => {
let mut $temp = Vec::new();
);
($temp:ident: Option) => (
};
($temp:ident: Option) => {
let mut $temp = None;
);
($temp:ident: Required) => (
};
($temp:ident: Required) => {
let mut $temp = None;
);
};
}
macro_rules! do_parse {
($elem:ident, Element) => (
($elem:ident, Element) => {
$elem.clone()
);
($elem:ident, String) => (
};
($elem:ident, String) => {
$elem.text()
);
($elem:ident, $constructor:ident) => (
};
($elem:ident, $constructor:ident) => {
$constructor::try_from($elem.clone())?
);
};
}
macro_rules! do_parse_elem {
($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => (
($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
$temp.push(do_parse!($elem, $constructor));
);
($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => (
};
($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
if $temp.is_some() {
return Err(crate::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child.")));
return Err(crate::error::Error::ParseError(concat!(
"Element ",
$parent_name,
" must not have more than one ",
$name,
" child."
)));
}
$temp = Some(do_parse!($elem, $constructor));
);
($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => (
};
($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
if $temp.is_some() {
return Err(crate::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child.")));
return Err(crate::error::Error::ParseError(concat!(
"Element ",
$parent_name,
" must not have more than one ",
$name,
" child."
)));
}
$temp = Some(do_parse!($elem, $constructor));
);
};
}
macro_rules! finish_parse_elem {
($temp:ident: Vec = $name:tt, $parent_name:tt) => (
($temp:ident: Vec = $name:tt, $parent_name:tt) => {
$temp
);
($temp:ident: Option = $name:tt, $parent_name:tt) => (
};
($temp:ident: Option = $name:tt, $parent_name:tt) => {
$temp
);
($temp:ident: Required = $name:tt, $parent_name:tt) => (
$temp.ok_or(crate::error::Error::ParseError(concat!("Missing child ", $name, " in ", $parent_name, " element.")))?
);
};
($temp:ident: Required = $name:tt, $parent_name:tt) => {
$temp.ok_or(crate::error::Error::ParseError(concat!(
"Missing child ",
$name,
" in ",
$parent_name,
" element."
)))?
};
}
macro_rules! generate_serialiser {
($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => (
($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => {
::minidom::Element::builder($name)
.ns(crate::ns::$ns)
.append($parent.$elem)
.build()
);
($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => (
$parent.$elem.map(|elem|
};
($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => {
$parent.$elem.map(|elem| {
::minidom::Element::builder($name)
.ns(crate::ns::$ns)
.append(elem)
.build())
);
($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => (
.build()
})
};
($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => {
$parent.$elem
);
};
}
macro_rules! generate_element {

View File

@ -4,21 +4,17 @@
// 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 try_from::TryFrom;
use minidom::Element;
use jid::Jid;
use crate::error::Error;
use crate::message::MessagePayload;
use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload};
use crate::data_forms::DataForm;
use crate::rsm::{SetQuery, SetResult};
use crate::error::Error;
use crate::forwarding::Forwarded;
use crate::pubsub::NodeName;
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
use crate::message::MessagePayload;
use crate::ns;
use crate::pubsub::NodeName;
use crate::rsm::{SetQuery, SetResult};
use jid::Jid;
use minidom::Element;
use try_from::TryFrom;
generate_id!(
/// An identifier matching a result message to the query requesting it.
@ -70,7 +66,9 @@ impl MessagePayload for Result_ {}
generate_attribute!(
/// True when the end of a MAM query has been reached.
Complete, "complete", bool
Complete,
"complete",
bool
);
generate_element!(
@ -133,8 +131,8 @@ impl TryFrom<Element> for Prefs {
fn try_from(elem: Element) -> Result<Prefs, Error> {
check_self!(elem, "prefs", MAM);
check_no_unknown_attributes!(elem, "prefs", ["default"]);
let mut always = vec!();
let mut never = vec!();
let mut always = vec![];
let mut never = vec![];
for child in elem.children() {
if child.is("always", ns::MAM) {
for jid_elem in child.children() {
@ -155,7 +153,11 @@ impl TryFrom<Element> for Prefs {
}
}
let default_ = get_attr!(elem, "default", required);
Ok(Prefs { default_, always, never })
Ok(Prefs {
default_,
always,
never,
})
}
}
@ -163,26 +165,27 @@ fn serialise_jid_list(name: &str, jids: Vec<Jid>) -> Option<Element> {
if jids.is_empty() {
None
} else {
Some(Element::builder(name)
.ns(ns::MAM)
.append(jids.into_iter()
.map(|jid| Element::builder("jid")
.ns(ns::MAM)
.append(jid)
.build())
.collect::<Vec<_>>())
.build())
Some(
Element::builder(name)
.ns(ns::MAM)
.append(
jids.into_iter()
.map(|jid| Element::builder("jid").ns(ns::MAM).append(jid).build())
.collect::<Vec<_>>(),
)
.build(),
)
}
}
impl From<Prefs> for Element {
fn from(prefs: Prefs) -> Element {
Element::builder("prefs")
.ns(ns::MAM)
.attr("default", prefs.default_)
.append(serialise_jid_list("always", prefs.always))
.append(serialise_jid_list("never", prefs.never))
.build()
.ns(ns::MAM)
.attr("default", prefs.default_)
.append(serialise_jid_list("always", prefs.always))
.append(serialise_jid_list("never", prefs.never))
.build()
}
}
@ -233,7 +236,9 @@ mod tests {
</message>
</forwarded>
</result>
"#.parse().unwrap();
"#
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = r#"
<result xmlns='urn:xmpp:mam:2' queryid='f27' id='28482-98726-73623'>
@ -257,7 +262,9 @@ mod tests {
<last>09af3-cc343-b409f</last>
</set>
</fin>
"#.parse().unwrap();
"#
.parse()
.unwrap();
Fin::try_from(elem).unwrap();
}
@ -274,7 +281,9 @@ mod tests {
</field>
</x>
</query>
"#.parse().unwrap();
"#
.parse()
.unwrap();
Query::try_from(elem).unwrap();
}
@ -294,13 +303,17 @@ mod tests {
<max>10</max>
</set>
</query>
"#.parse().unwrap();
"#
.parse()
.unwrap();
Query::try_from(elem).unwrap();
}
#[test]
fn test_prefs_get() {
let elem: Element = "<prefs xmlns='urn:xmpp:mam:2' default='always'/>".parse().unwrap();
let elem: Element = "<prefs xmlns='urn:xmpp:mam:2' default='always'/>"
.parse()
.unwrap();
let prefs = Prefs::try_from(elem).unwrap();
assert_eq!(prefs.always, vec!());
assert_eq!(prefs.never, vec!());
@ -310,7 +323,9 @@ mod tests {
<always/>
<never/>
</prefs>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let prefs = Prefs::try_from(elem).unwrap();
assert_eq!(prefs.always, vec!());
assert_eq!(prefs.never, vec!());
@ -327,10 +342,18 @@ mod tests {
<jid>montague@montague.lit</jid>
</never>
</prefs>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let prefs = Prefs::try_from(elem).unwrap();
assert_eq!(prefs.always, vec!(Jid::from_str("romeo@montague.lit").unwrap()));
assert_eq!(prefs.never, vec!(Jid::from_str("montague@montague.lit").unwrap()));
assert_eq!(
prefs.always,
vec!(Jid::from_str("romeo@montague.lit").unwrap())
);
assert_eq!(
prefs.never,
vec!(Jid::from_str("montague@montague.lit").unwrap())
);
let elem2 = Element::from(prefs.clone());
println!("{:?}", elem2);
@ -342,7 +365,9 @@ mod tests {
#[test]
fn test_invalid_child() {
let elem: Element = "<query xmlns='urn:xmpp:mam:2'><coucou/></query>".parse().unwrap();
let elem: Element = "<query xmlns='urn:xmpp:mam:2'><coucou/></query>"
.parse()
.unwrap();
let error = Query::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -354,7 +379,12 @@ mod tests {
#[test]
fn test_serialise() {
let elem: Element = "<query xmlns='urn:xmpp:mam:2'/>".parse().unwrap();
let replace = Query { queryid: None, node: None, form: None, set: None };
let replace = Query {
queryid: None,
node: None,
form: None,
set: None,
};
let elem2 = replace.into();
assert_eq!(elem, elem2);
}

View File

@ -45,11 +45,11 @@ generate_element!(
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use crate::data_forms::DataForm;
use crate::error::Error;
use minidom::Element;
use std::error::Error as StdError;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -76,7 +76,9 @@ mod tests {
#[test]
fn test_width_height() {
let elem: Element = "<media xmlns='urn:xmpp:media-element' width='32' height='32'/>".parse().unwrap();
let elem: Element = "<media xmlns='urn:xmpp:media-element' width='32' height='32'/>"
.parse()
.unwrap();
let media = MediaElement::try_from(elem).unwrap();
assert_eq!(media.width.unwrap(), 32);
assert_eq!(media.height.unwrap(), 32);
@ -93,15 +95,22 @@ mod tests {
#[test]
fn test_invalid_width_height() {
let elem: Element = "<media xmlns='urn:xmpp:media-element' width=''/>".parse().unwrap();
let elem: Element = "<media xmlns='urn:xmpp:media-element' width=''/>"
.parse()
.unwrap();
let error = MediaElement::try_from(elem).unwrap_err();
let error = match error {
Error::ParseIntError(error) => error,
_ => panic!(),
};
assert_eq!(error.description(), "cannot parse integer from empty string");
assert_eq!(
error.description(),
"cannot parse integer from empty string"
);
let elem: Element = "<media xmlns='urn:xmpp:media-element' width='coucou'/>".parse().unwrap();
let elem: Element = "<media xmlns='urn:xmpp:media-element' width='coucou'/>"
.parse()
.unwrap();
let error = MediaElement::try_from(elem).unwrap_err();
let error = match error {
Error::ParseIntError(error) => error,
@ -109,15 +118,22 @@ mod tests {
};
assert_eq!(error.description(), "invalid digit found in string");
let elem: Element = "<media xmlns='urn:xmpp:media-element' height=''/>".parse().unwrap();
let elem: Element = "<media xmlns='urn:xmpp:media-element' height=''/>"
.parse()
.unwrap();
let error = MediaElement::try_from(elem).unwrap_err();
let error = match error {
Error::ParseIntError(error) => error,
_ => panic!(),
};
assert_eq!(error.description(), "cannot parse integer from empty string");
assert_eq!(
error.description(),
"cannot parse integer from empty string"
);
let elem: Element = "<media xmlns='urn:xmpp:media-element' height='-10'/>".parse().unwrap();
let elem: Element = "<media xmlns='urn:xmpp:media-element' height='-10'/>"
.parse()
.unwrap();
let error = MediaElement::try_from(elem).unwrap_err();
let error = match error {
Error::ParseIntError(error) => error,
@ -128,7 +144,9 @@ mod tests {
#[test]
fn test_unknown_child() {
let elem: Element = "<media xmlns='urn:xmpp:media-element'><coucou/></media>".parse().unwrap();
let elem: Element = "<media xmlns='urn:xmpp:media-element'><coucou/></media>"
.parse()
.unwrap();
let error = MediaElement::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -139,7 +157,10 @@ mod tests {
#[test]
fn test_bad_uri() {
let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri>https://example.org/</uri></media>".parse().unwrap();
let elem: Element =
"<media xmlns='urn:xmpp:media-element'><uri>https://example.org/</uri></media>"
.parse()
.unwrap();
let error = MediaElement::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -147,7 +168,9 @@ mod tests {
};
assert_eq!(message, "Required attribute 'type' missing.");
let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'/></media>".parse().unwrap();
let elem: Element = "<media xmlns='urn:xmpp:media-element'><uri type='text/html'/></media>"
.parse()
.unwrap();
let error = MediaElement::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -169,17 +192,28 @@ mod tests {
<uri type='audio/mpeg'>
http://victim.example.com/challenges/speech.mp3?F3A6292C
</uri>
</media>"#.parse().unwrap();
</media>"#
.parse()
.unwrap();
let media = MediaElement::try_from(elem).unwrap();
assert!(media.width.is_none());
assert!(media.height.is_none());
assert_eq!(media.uris.len(), 3);
assert_eq!(media.uris[0].type_, "audio/x-wav");
assert_eq!(media.uris[0].uri, "http://victim.example.com/challenges/speech.wav?F3A6292C");
assert_eq!(
media.uris[0].uri,
"http://victim.example.com/challenges/speech.wav?F3A6292C"
);
assert_eq!(media.uris[1].type_, "audio/ogg; codecs=speex");
assert_eq!(media.uris[1].uri, "cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org");
assert_eq!(
media.uris[1].uri,
"cid:sha1+a15a505e360702b79c75a5f67773072ed392f52a@bob.xmpp.org"
);
assert_eq!(media.uris[2].type_, "audio/mpeg");
assert_eq!(media.uris[2].uri, "http://victim.example.com/challenges/speech.mp3?F3A6292C");
assert_eq!(
media.uris[2].uri,
"http://victim.example.com/challenges/speech.mp3?F3A6292C"
);
}
#[test]
@ -200,15 +234,23 @@ mod tests {
</media>
</field>
[ ... ]
</x>"#.parse().unwrap();
</x>"#
.parse()
.unwrap();
let form = DataForm::try_from(elem).unwrap();
assert_eq!(form.fields.len(), 1);
assert_eq!(form.fields[0].var, "ocr");
assert_eq!(form.fields[0].media[0].width, Some(290));
assert_eq!(form.fields[0].media[0].height, Some(80));
assert_eq!(form.fields[0].media[0].uris[0].type_, "image/jpeg");
assert_eq!(form.fields[0].media[0].uris[0].uri, "http://www.victim.com/challenges/ocr.jpeg?F3A6292C");
assert_eq!(
form.fields[0].media[0].uris[0].uri,
"http://www.victim.com/challenges/ocr.jpeg?F3A6292C"
);
assert_eq!(form.fields[0].media[0].uris[1].type_, "image/jpeg");
assert_eq!(form.fields[0].media[0].uris[1].uri, "cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org");
assert_eq!(
form.fields[0].media[0].uris[1].uri,
"cid:sha1+f24030b8d91d233bac14777be5ab531ca3b9f102@bob.xmpp.org"
);
}
}

View File

@ -4,16 +4,12 @@
// 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 try_from::TryFrom;
use std::collections::BTreeMap;
use minidom::Element;
use jid::Jid;
use crate::error::Error;
use crate::ns;
use jid::Jid;
use minidom::Element;
use std::collections::BTreeMap;
use try_from::TryFrom;
/// Should be implemented on every known payload of a `<message/>`.
pub trait MessagePayload: TryFrom<Element> + Into<Element> {}
@ -44,18 +40,24 @@ type Lang = String;
generate_elem_id!(
/// Represents one `<body/>` element, that is the free form text content of
/// a message.
Body, "body", DEFAULT_NS
Body,
"body",
DEFAULT_NS
);
generate_elem_id!(
/// Defines the subject of a room, or of an email-like normal message.
Subject, "subject", DEFAULT_NS
Subject,
"subject",
DEFAULT_NS
);
generate_elem_id!(
/// A thread identifier, so that other people can specify to which message
/// they are replying.
Thread, "thread", DEFAULT_NS
Thread,
"thread",
DEFAULT_NS
);
/// The main structure representing the `<message/>` stanza.
@ -102,11 +104,14 @@ impl Message {
bodies: BTreeMap::new(),
subjects: BTreeMap::new(),
thread: None,
payloads: vec!(),
payloads: vec![],
}
}
fn get_best<'a, T>(map: &'a BTreeMap<Lang, T>, preferred_langs: Vec<&str>) -> Option<(Lang, &'a T)> {
fn get_best<'a, T>(
map: &'a BTreeMap<Lang, T>,
preferred_langs: Vec<&str>,
) -> Option<(Lang, &'a T)> {
if map.is_empty() {
return None;
}
@ -156,21 +161,25 @@ impl TryFrom<Element> for Message {
let mut bodies = BTreeMap::new();
let mut subjects = BTreeMap::new();
let mut thread = None;
let mut payloads = vec!();
let mut payloads = vec![];
for elem in root.children() {
if elem.is("body", ns::DEFAULT_NS) {
check_no_children!(elem, "body");
let lang = get_attr!(elem, "xml:lang", default);
let body = Body(elem.text());
if bodies.insert(lang, body).is_some() {
return Err(Error::ParseError("Body element present twice for the same xml:lang."));
return Err(Error::ParseError(
"Body element present twice for the same xml:lang.",
));
}
} else if elem.is("subject", ns::DEFAULT_NS) {
check_no_children!(elem, "subject");
let lang = get_attr!(elem, "xml:lang", default);
let subject = Subject(elem.text());
if subjects.insert(lang, subject).is_some() {
return Err(Error::ParseError("Subject element present twice for the same xml:lang."));
return Err(Error::ParseError(
"Subject element present twice for the same xml:lang.",
));
}
} else if elem.is("thread", ns::DEFAULT_NS) {
if thread.is_some() {
@ -198,41 +207,55 @@ impl TryFrom<Element> for Message {
impl From<Message> for Element {
fn from(message: Message) -> Element {
Element::builder("message")
.ns(ns::DEFAULT_NS)
.attr("from", message.from)
.attr("to", message.to)
.attr("id", message.id)
.attr("type", message.type_)
.append(message.subjects.into_iter()
.map(|(lang, subject)| {
let mut subject = Element::from(subject);
subject.set_attr("xml:lang", match lang.as_ref() {
"" => None,
lang => Some(lang),
});
subject
})
.collect::<Vec<_>>())
.append(message.bodies.into_iter()
.map(|(lang, body)| {
let mut body = Element::from(body);
body.set_attr("xml:lang", match lang.as_ref() {
"" => None,
lang => Some(lang),
});
body
})
.collect::<Vec<_>>())
.append(message.payloads)
.build()
.ns(ns::DEFAULT_NS)
.attr("from", message.from)
.attr("to", message.to)
.attr("id", message.id)
.attr("type", message.type_)
.append(
message
.subjects
.into_iter()
.map(|(lang, subject)| {
let mut subject = Element::from(subject);
subject.set_attr(
"xml:lang",
match lang.as_ref() {
"" => None,
lang => Some(lang),
},
);
subject
})
.collect::<Vec<_>>(),
)
.append(
message
.bodies
.into_iter()
.map(|(lang, body)| {
let mut body = Element::from(body);
body.set_attr(
"xml:lang",
match lang.as_ref() {
"" => None,
lang => Some(lang),
},
);
body
})
.collect::<Vec<_>>(),
)
.append(message.payloads)
.build()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
use crate::compare_elements::NamespaceAwareCompare;
use std::str::FromStr;
#[cfg(target_pointer_width = "32")]
#[test]
@ -259,7 +282,9 @@ mod tests {
#[cfg(not(feature = "component"))]
let elem: Element = "<message xmlns='jabber:client'/>".parse().unwrap();
#[cfg(feature = "component")]
let elem: Element = "<message xmlns='jabber:component:accept'/>".parse().unwrap();
let elem: Element = "<message xmlns='jabber:component:accept'/>"
.parse()
.unwrap();
let message = Message::try_from(elem).unwrap();
assert_eq!(message.from, None);
assert_eq!(message.to, None);
@ -273,7 +298,9 @@ mod tests {
#[cfg(not(feature = "component"))]
let elem: Element = "<message xmlns='jabber:client'/>".parse().unwrap();
#[cfg(feature = "component")]
let elem: Element = "<message xmlns='jabber:component:accept'/>".parse().unwrap();
let elem: Element = "<message xmlns='jabber:component:accept'/>"
.parse()
.unwrap();
let mut message = Message::new(None);
message.type_ = MessageType::Normal;
let elem2 = message.into();
@ -291,7 +318,7 @@ mod tests {
assert_eq!(message.bodies[""], Body::from_str("Hello world!").unwrap());
{
let (lang, body) = message.get_best_body(vec!("en")).unwrap();
let (lang, body) = message.get_best_body(vec!["en"]).unwrap();
assert_eq!(lang, "");
assert_eq!(body, &Body::from_str("Hello world!").unwrap());
}
@ -307,7 +334,9 @@ mod tests {
#[cfg(feature = "component")]
let elem: Element = "<message xmlns='jabber:component:accept' to='coucou@example.org' type='chat'><body>Hello world!</body></message>".parse().unwrap();
let mut message = Message::new(Some(Jid::from_str("coucou@example.org").unwrap()));
message.bodies.insert(String::from(""), Body::from_str("Hello world!").unwrap());
message
.bodies
.insert(String::from(""), Body::from_str("Hello world!").unwrap());
let elem2 = message.into();
assert!(elem.compare_to(&elem2));
}
@ -320,10 +349,13 @@ mod tests {
let elem: Element = "<message xmlns='jabber:component:accept' to='coucou@example.org' type='chat'><subject>Hello world!</subject></message>".parse().unwrap();
let elem1 = elem.clone();
let message = Message::try_from(elem).unwrap();
assert_eq!(message.subjects[""], Subject::from_str("Hello world!").unwrap());
assert_eq!(
message.subjects[""],
Subject::from_str("Hello world!").unwrap()
);
{
let (lang, subject) = message.get_best_subject(vec!("en")).unwrap();
let (lang, subject) = message.get_best_subject(vec!["en"]).unwrap();
assert_eq!(lang, "");
assert_eq!(subject, &Subject::from_str("Hello world!").unwrap());
}
@ -342,28 +374,28 @@ mod tests {
// Tests basic feature.
{
let (lang, body) = message.get_best_body(vec!("fr")).unwrap();
let (lang, body) = message.get_best_body(vec!["fr"]).unwrap();
assert_eq!(lang, "fr");
assert_eq!(body, &Body::from_str("Salut le monde!").unwrap());
}
// Tests order.
{
let (lang, body) = message.get_best_body(vec!("en", "de")).unwrap();
let (lang, body) = message.get_best_body(vec!["en", "de"]).unwrap();
assert_eq!(lang, "de");
assert_eq!(body, &Body::from_str("Hallo Welt!").unwrap());
}
// Tests fallback.
{
let (lang, body) = message.get_best_body(vec!()).unwrap();
let (lang, body) = message.get_best_body(vec![]).unwrap();
assert_eq!(lang, "");
assert_eq!(body, &Body::from_str("Hello world!").unwrap());
}
// Tests fallback.
{
let (lang, body) = message.get_best_body(vec!("ja")).unwrap();
let (lang, body) = message.get_best_body(vec!["ja"]).unwrap();
assert_eq!(lang, "");
assert_eq!(body, &Body::from_str("Hello world!").unwrap());
}

View File

@ -21,9 +21,9 @@ impl MessagePayload for Replace {}
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use minidom::Element;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -39,13 +39,17 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0' id='coucou'/>".parse().unwrap();
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0' id='coucou'/>"
.parse()
.unwrap();
Replace::try_from(elem).unwrap();
}
#[test]
fn test_invalid_attribute() {
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0' coucou=''/>".parse().unwrap();
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0' coucou=''/>"
.parse()
.unwrap();
let error = Replace::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -56,7 +60,9 @@ mod tests {
#[test]
fn test_invalid_child() {
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'><coucou/></replace>".parse().unwrap();
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'><coucou/></replace>"
.parse()
.unwrap();
let error = Replace::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -67,7 +73,9 @@ mod tests {
#[test]
fn test_invalid_id() {
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>".parse().unwrap();
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
.parse()
.unwrap();
let error = Replace::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -78,8 +86,12 @@ mod tests {
#[test]
fn test_serialise() {
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0' id='coucou'/>".parse().unwrap();
let replace = Replace { id: String::from("coucou") };
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0' id='coucou'/>"
.parse()
.unwrap();
let replace = Replace {
id: String::from("coucou"),
};
let elem2 = replace.into();
assert_eq!(elem, elem2);
}

View File

@ -263,14 +263,16 @@ generate_element_enum!(
generate_elem_id!(
/// Free-form text description of the mood.
Text, "text", MOOD
Text,
"text",
MOOD
);
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -288,14 +290,18 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<happy xmlns='http://jabber.org/protocol/mood'/>".parse().unwrap();
let elem: Element = "<happy xmlns='http://jabber.org/protocol/mood'/>"
.parse()
.unwrap();
let mood = MoodEnum::try_from(elem).unwrap();
assert_eq!(mood, MoodEnum::Happy);
}
#[test]
fn test_text() {
let elem: Element = "<text xmlns='http://jabber.org/protocol/mood'>Yay!</text>".parse().unwrap();
let elem: Element = "<text xmlns='http://jabber.org/protocol/mood'>Yay!</text>"
.parse()
.unwrap();
let text = Text::try_from(elem).unwrap();
assert_eq!(text.0, String::from("Yay!"));
}

View File

@ -5,8 +5,8 @@
// 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 crate::presence::PresencePayload;
use crate::date::DateTime;
use crate::presence::PresencePayload;
generate_element!(
/// Represents the query for messages before our join.
@ -102,21 +102,25 @@ impl Muc {
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use std::str::FromStr;
use crate::compare_elements::NamespaceAwareCompare;
use crate::error::Error;
use minidom::Element;
use std::str::FromStr;
use try_from::TryFrom;
#[test]
fn test_muc_simple() {
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>".parse().unwrap();
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>"
.parse()
.unwrap();
Muc::try_from(elem).unwrap();
}
#[test]
fn test_muc_invalid_child() {
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'><coucou/></x>".parse().unwrap();
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'><coucou/></x>"
.parse()
.unwrap();
let error = Muc::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -127,7 +131,9 @@ mod tests {
#[test]
fn test_muc_serialise() {
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>".parse().unwrap();
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc'/>"
.parse()
.unwrap();
let muc = Muc {
password: None,
history: None,
@ -138,7 +144,9 @@ mod tests {
#[test]
fn test_muc_invalid_attribute() {
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc' coucou=''/>".parse().unwrap();
let elem: Element = "<x xmlns='http://jabber.org/protocol/muc' coucou=''/>"
.parse()
.unwrap();
let error = Muc::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -153,7 +161,8 @@ mod tests {
<x xmlns='http://jabber.org/protocol/muc'>
<password>coucou</password>
</x>"
.parse().unwrap();
.parse()
.unwrap();
let elem1 = elem.clone();
let muc = Muc::try_from(elem).unwrap();
assert_eq!(muc.password, Some("coucou".to_owned()));
@ -168,7 +177,8 @@ mod tests {
<x xmlns='http://jabber.org/protocol/muc'>
<history maxstanzas='0'/>
</x>"
.parse().unwrap();
.parse()
.unwrap();
let muc = Muc::try_from(elem).unwrap();
let muc2 = Muc::new().with_history(History::new().with_maxstanzas(0));
assert_eq!(muc, muc2);
@ -183,8 +193,12 @@ mod tests {
<x xmlns='http://jabber.org/protocol/muc'>
<history since='1970-01-01T00:00:00Z'/>
</x>"
.parse().unwrap();
.parse()
.unwrap();
let muc = Muc::try_from(elem).unwrap();
assert_eq!(muc.history.unwrap().since.unwrap(), DateTime::from_str("1970-01-01T00:00:00+00:00").unwrap());
assert_eq!(
muc.history.unwrap().since.unwrap(),
DateTime::from_str("1970-01-01T00:00:00+00:00").unwrap()
);
}
}

View File

@ -5,15 +5,11 @@
// 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 try_from::TryFrom;
use minidom::Element;
use jid::Jid;
use crate::error::Error;
use crate::ns;
use jid::Jid;
use minidom::Element;
use try_from::TryFrom;
generate_attribute_enum!(
/// Lists all of the possible status codes used in MUC presences.
@ -103,9 +99,11 @@ impl TryFrom<Element> for Actor {
let nick = get_attr!(elem, "nick", optional);
match (jid, nick) {
(Some(_), Some(_))
| (None, None) =>
return Err(Error::ParseError("Either 'jid' or 'nick' attribute is required.")),
(Some(_), Some(_)) | (None, None) => {
return Err(Error::ParseError(
"Either 'jid' or 'nick' attribute is required.",
))
}
(Some(jid), _) => Ok(Actor::Jid(jid)),
(_, Some(nick)) => Ok(Actor::Nick(nick)),
}
@ -119,7 +117,8 @@ impl From<Actor> for Element {
(match actor {
Actor::Jid(jid) => elem.attr("jid", jid),
Actor::Nick(nick) => elem.attr("nick", nick),
}).build()
})
.build()
}
}
@ -135,7 +134,9 @@ generate_element!(
generate_elem_id!(
/// A reason for inviting, declining, etc. a request.
Reason, "reason", MUC_USER
Reason,
"reason",
MUC_USER
);
generate_attribute!(
@ -237,14 +238,16 @@ generate_element!(
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error as StdError;
use crate::compare_elements::NamespaceAwareCompare;
use std::error::Error as StdError;
#[test]
fn test_simple() {
let elem: Element = "
<x xmlns='http://jabber.org/protocol/muc#user'/>
".parse().unwrap();
"
.parse()
.unwrap();
MucUser::try_from(elem).unwrap();
}
@ -256,7 +259,9 @@ mod tests {
<status code='102'/>
<item affiliation='member' role='moderator'/>
</x>
".parse().unwrap();
"
.parse()
.unwrap();
let muc_user = MucUser::try_from(elem).unwrap();
assert_eq!(muc_user.status.len(), 2);
assert_eq!(muc_user.status[0], Status::AffiliationChange);
@ -272,7 +277,9 @@ mod tests {
<x xmlns='http://jabber.org/protocol/muc#user'>
<coucou/>
</x>
".parse().unwrap();
"
.parse()
.unwrap();
let error = MucUser::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -285,8 +292,13 @@ mod tests {
fn test_serialise() {
let elem: Element = "
<x xmlns='http://jabber.org/protocol/muc#user'/>
".parse().unwrap();
let muc = MucUser { status: vec!(), items: vec!() };
"
.parse()
.unwrap();
let muc = MucUser {
status: vec![],
items: vec![],
};
let elem2 = muc.into();
assert!(elem.compare_to(&elem2));
}
@ -295,7 +307,9 @@ mod tests {
fn test_invalid_attribute() {
let elem: Element = "
<x xmlns='http://jabber.org/protocol/muc#user' coucou=''/>
".parse().unwrap();
"
.parse()
.unwrap();
let error = MucUser::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -308,7 +322,9 @@ mod tests {
fn test_status_simple() {
let elem: Element = "
<status xmlns='http://jabber.org/protocol/muc#user' code='110'/>
".parse().unwrap();
"
.parse()
.unwrap();
Status::try_from(elem).unwrap();
}
@ -316,7 +332,9 @@ mod tests {
fn test_status_invalid() {
let elem: Element = "
<status xmlns='http://jabber.org/protocol/muc#user'/>
".parse().unwrap();
"
.parse()
.unwrap();
let error = Status::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -331,7 +349,9 @@ mod tests {
<status xmlns='http://jabber.org/protocol/muc#user' code='110'>
<foo/>
</status>
".parse().unwrap();
"
.parse()
.unwrap();
let error = Status::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -344,7 +364,9 @@ mod tests {
fn test_status_simple_code() {
let elem: Element = "
<status xmlns='http://jabber.org/protocol/muc#user' code='307'/>
".parse().unwrap();
"
.parse()
.unwrap();
let status = Status::try_from(elem).unwrap();
assert_eq!(status, Status::Kicked);
}
@ -353,7 +375,9 @@ mod tests {
fn test_status_invalid_code() {
let elem: Element = "
<status xmlns='http://jabber.org/protocol/muc#user' code='666'/>
".parse().unwrap();
"
.parse()
.unwrap();
let error = Status::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -366,7 +390,9 @@ mod tests {
fn test_status_invalid_code2() {
let elem: Element = "
<status xmlns='http://jabber.org/protocol/muc#user' code='coucou'/>
".parse().unwrap();
"
.parse()
.unwrap();
let error = Status::try_from(elem).unwrap_err();
let error = match error {
Error::ParseIntError(error) => error,
@ -379,7 +405,9 @@ mod tests {
fn test_actor_required_attributes() {
let elem: Element = "
<actor xmlns='http://jabber.org/protocol/muc#user'/>
".parse().unwrap();
"
.parse()
.unwrap();
let error = Actor::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -394,7 +422,9 @@ mod tests {
<actor xmlns='http://jabber.org/protocol/muc#user'
jid='foo@bar/baz'
nick='baz'/>
".parse().unwrap();
"
.parse()
.unwrap();
let error = Actor::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -408,7 +438,9 @@ mod tests {
let elem: Element = "
<actor xmlns='http://jabber.org/protocol/muc#user'
jid='foo@bar/baz'/>
".parse().unwrap();
"
.parse()
.unwrap();
let actor = Actor::try_from(elem).unwrap();
let jid = match actor {
Actor::Jid(jid) => jid,
@ -421,7 +453,9 @@ mod tests {
fn test_actor_nick() {
let elem: Element = "
<actor xmlns='http://jabber.org/protocol/muc#user' nick='baz'/>
".parse().unwrap();
"
.parse()
.unwrap();
let actor = Actor::try_from(elem).unwrap();
let nick = match actor {
Actor::Nick(nick) => nick,
@ -434,7 +468,9 @@ mod tests {
fn test_continue_simple() {
let elem: Element = "
<continue xmlns='http://jabber.org/protocol/muc#user'/>
".parse().unwrap();
"
.parse()
.unwrap();
Continue::try_from(elem).unwrap();
}
@ -443,7 +479,9 @@ mod tests {
let elem: Element = "
<continue xmlns='http://jabber.org/protocol/muc#user'
thread='foo'/>
".parse().unwrap();
"
.parse()
.unwrap();
let continue_ = Continue::try_from(elem).unwrap();
assert_eq!(continue_.thread, Some("foo".to_owned()));
}
@ -454,7 +492,9 @@ mod tests {
<continue xmlns='http://jabber.org/protocol/muc#user'>
<foobar/>
</continue>
".parse().unwrap();
"
.parse()
.unwrap();
let continue_ = Continue::try_from(elem).unwrap_err();
let message = match continue_ {
Error::ParseError(string) => string,
@ -467,7 +507,8 @@ mod tests {
fn test_reason_simple() {
let elem: Element = "
<reason xmlns='http://jabber.org/protocol/muc#user'>Reason</reason>"
.parse().unwrap();
.parse()
.unwrap();
let reason = Reason::try_from(elem).unwrap();
assert_eq!(reason.0, "Reason".to_owned());
}
@ -476,7 +517,9 @@ mod tests {
fn test_reason_invalid_attribute() {
let elem: Element = "
<reason xmlns='http://jabber.org/protocol/muc#user' foo='bar'/>
".parse().unwrap();
"
.parse()
.unwrap();
let error = Reason::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -491,7 +534,9 @@ mod tests {
<reason xmlns='http://jabber.org/protocol/muc#user'>
<foobar/>
</reason>
".parse().unwrap();
"
.parse()
.unwrap();
let error = Reason::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -501,11 +546,13 @@ mod tests {
}
#[test]
fn test_item_invalid_attr(){
fn test_item_invalid_attr() {
let elem: Element = "
<item xmlns='http://jabber.org/protocol/muc#user'
foo='bar'/>
".parse().unwrap();
"
.parse()
.unwrap();
let error = Item::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -515,21 +562,25 @@ mod tests {
}
#[test]
fn test_item_affiliation_role_attr(){
fn test_item_affiliation_role_attr() {
let elem: Element = "
<item xmlns='http://jabber.org/protocol/muc#user'
affiliation='member'
role='moderator'/>
".parse().unwrap();
"
.parse()
.unwrap();
Item::try_from(elem).unwrap();
}
#[test]
fn test_item_affiliation_role_invalid_attr(){
fn test_item_affiliation_role_invalid_attr() {
let elem: Element = "
<item xmlns='http://jabber.org/protocol/muc#user'
affiliation='member'/>
".parse().unwrap();
"
.parse()
.unwrap();
let error = Item::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -539,13 +590,15 @@ mod tests {
}
#[test]
fn test_item_nick_attr(){
fn test_item_nick_attr() {
let elem: Element = "
<item xmlns='http://jabber.org/protocol/muc#user'
affiliation='member'
role='moderator'
nick='foobar'/>
".parse().unwrap();
"
.parse()
.unwrap();
let item = Item::try_from(elem).unwrap();
match item {
Item { nick, .. } => assert_eq!(nick, Some("foobar".to_owned())),
@ -553,65 +606,79 @@ mod tests {
}
#[test]
fn test_item_affiliation_role_invalid_attr2(){
fn test_item_affiliation_role_invalid_attr2() {
let elem: Element = "
<item xmlns='http://jabber.org/protocol/muc#user'
role='moderator'/>
".parse().unwrap();
"
.parse()
.unwrap();
let error = Item::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
_ => panic!(),
};
assert_eq!(message, "Required attribute 'affiliation' missing.".to_owned());
assert_eq!(
message,
"Required attribute 'affiliation' missing.".to_owned()
);
}
#[test]
fn test_item_role_actor_child(){
fn test_item_role_actor_child() {
let elem: Element = "
<item xmlns='http://jabber.org/protocol/muc#user'
affiliation='member'
role='moderator'>
<actor nick='foobar'/>
</item>
".parse().unwrap();
"
.parse()
.unwrap();
let item = Item::try_from(elem).unwrap();
match item {
Item { actor, .. } =>
assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))),
Item { actor, .. } => assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))),
}
}
#[test]
fn test_item_role_continue_child(){
fn test_item_role_continue_child() {
let elem: Element = "
<item xmlns='http://jabber.org/protocol/muc#user'
affiliation='member'
role='moderator'>
<continue thread='foobar'/>
</item>
".parse().unwrap();
"
.parse()
.unwrap();
let item = Item::try_from(elem).unwrap();
let continue_1 = Continue { thread: Some("foobar".to_owned()) };
let continue_1 = Continue {
thread: Some("foobar".to_owned()),
};
match item {
Item { continue_: Some(continue_2), .. } => assert_eq!(continue_2.thread, continue_1.thread),
Item {
continue_: Some(continue_2),
..
} => assert_eq!(continue_2.thread, continue_1.thread),
_ => panic!(),
}
}
#[test]
fn test_item_role_reason_child(){
fn test_item_role_reason_child() {
let elem: Element = "
<item xmlns='http://jabber.org/protocol/muc#user'
affiliation='member'
role='moderator'>
<reason>foobar</reason>
</item>
".parse().unwrap();
"
.parse()
.unwrap();
let item = Item::try_from(elem).unwrap();
match item {
Item { reason, .. } =>
assert_eq!(reason, Some(Reason("foobar".to_owned()))),
Item { reason, .. } => assert_eq!(reason, Some(Reason("foobar".to_owned()))),
}
}
}

View File

@ -6,15 +6,17 @@
generate_elem_id!(
/// Represents a global, memorable, friendly or informal name chosen by a user.
Nick, "nick", NICK
Nick,
"nick",
NICK
);
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use minidom::Element;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -30,7 +32,9 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick'>Link Mauve</nick>".parse().unwrap();
let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick'>Link Mauve</nick>"
.parse()
.unwrap();
let nick = Nick::try_from(elem).unwrap();
assert_eq!(&nick.0, "Link Mauve");
}
@ -38,13 +42,17 @@ mod tests {
#[test]
fn test_serialise() {
let elem1 = Element::from(Nick(String::from("Link Mauve")));
let elem2: Element = "<nick xmlns='http://jabber.org/protocol/nick'>Link Mauve</nick>".parse().unwrap();
let elem2: Element = "<nick xmlns='http://jabber.org/protocol/nick'>Link Mauve</nick>"
.parse()
.unwrap();
assert_eq!(elem1, elem2);
}
#[test]
fn test_invalid() {
let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick'><coucou/></nick>".parse().unwrap();
let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick'><coucou/></nick>"
.parse()
.unwrap();
let error = Nick::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -55,7 +63,9 @@ mod tests {
#[test]
fn test_invalid_attribute() {
let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick' coucou=''/>".parse().unwrap();
let elem: Element = "<nick xmlns='http://jabber.org/protocol/nick' coucou=''/>"
.parse()
.unwrap();
let error = Nick::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,

View File

@ -10,7 +10,9 @@ use crate::iq::IqGetPayload;
generate_empty_element!(
/// Represents a ping to the recipient, which must be answered with an
/// empty `<iq/>` or with an error.
Ping, "ping", PING
Ping,
"ping",
PING
);
impl IqGetPayload for Ping {}
@ -18,9 +20,9 @@ impl IqGetPayload for Ping {}
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use minidom::Element;
use try_from::TryFrom;
#[test]
fn test_size() {
@ -42,7 +44,9 @@ mod tests {
#[test]
fn test_invalid() {
let elem: Element = "<ping xmlns='urn:xmpp:ping'><coucou/></ping>".parse().unwrap();
let elem: Element = "<ping xmlns='urn:xmpp:ping'><coucou/></ping>"
.parse()
.unwrap();
let error = Ping::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,

View File

@ -5,11 +5,11 @@
// 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 try_from::TryFrom;
use std::str::FromStr;
use std::collections::BTreeMap;
use std::str::FromStr;
use try_from::TryFrom;
use minidom::{Element, IntoElements, IntoAttributeValue, ElementEmitter};
use minidom::{Element, ElementEmitter, IntoAttributeValue, IntoElements};
use jid::Jid;
@ -68,14 +68,15 @@ impl IntoElements for Show {
}
emitter.append_child(
Element::builder("show")
.append(match self {
Show::None => unreachable!(),
Show::Away => Some("away"),
Show::Chat => Some("chat"),
Show::Dnd => Some("dnd"),
Show::Xa => Some("xa"),
})
.build())
.append(match self {
Show::None => unreachable!(),
Show::Away => Some("away"),
Show::Chat => Some("chat"),
Show::Dnd => Some("dnd"),
Show::Xa => Some("xa"),
})
.build(),
)
}
}
@ -84,7 +85,7 @@ type Status = String;
type Priority = i8;
///
///
#[derive(Debug, Clone, PartialEq)]
pub enum Type {
/// This value is not an acceptable 'type' attribute, it is only used
@ -136,24 +137,31 @@ impl FromStr for Type {
"unsubscribe" => Type::Unsubscribe,
"unsubscribed" => Type::Unsubscribed,
_ => return Err(Error::ParseError("Invalid 'type' attribute on presence element.")),
_ => {
return Err(Error::ParseError(
"Invalid 'type' attribute on presence element.",
))
}
})
}
}
impl IntoAttributeValue for Type {
fn into_attribute_value(self) -> Option<String> {
Some(match self {
Type::None => return None,
Some(
match self {
Type::None => return None,
Type::Error => "error",
Type::Probe => "probe",
Type::Subscribe => "subscribe",
Type::Subscribed => "subscribed",
Type::Unavailable => "unavailable",
Type::Unsubscribe => "unsubscribe",
Type::Unsubscribed => "unsubscribed",
}.to_owned())
Type::Error => "error",
Type::Probe => "probe",
Type::Subscribe => "subscribe",
Type::Subscribed => "subscribed",
Type::Unavailable => "unavailable",
Type::Unsubscribe => "unsubscribe",
Type::Unsubscribed => "unsubscribed",
}
.to_owned(),
)
}
}
@ -197,7 +205,7 @@ impl Presence {
show: Show::None,
statuses: BTreeMap::new(),
priority: 0i8,
payloads: vec!(),
payloads: vec![],
}
}
@ -266,12 +274,14 @@ impl TryFrom<Element> for Presence {
show: Show::None,
statuses: BTreeMap::new(),
priority: 0i8,
payloads: vec!(),
payloads: vec![],
};
for elem in root.children() {
if elem.is("show", ns::DEFAULT_NS) {
if show.is_some() {
return Err(Error::ParseError("More than one show element in a presence."));
return Err(Error::ParseError(
"More than one show element in a presence.",
));
}
check_no_attributes!(elem, "show");
check_no_children!(elem, "show");
@ -281,11 +291,15 @@ impl TryFrom<Element> for Presence {
check_no_children!(elem, "status");
let lang = get_attr!(elem, "xml:lang", default);
if presence.statuses.insert(lang, elem.text()).is_some() {
return Err(Error::ParseError("Status element present twice for the same xml:lang."));
return Err(Error::ParseError(
"Status element present twice for the same xml:lang.",
));
}
} else if elem.is("priority", ns::DEFAULT_NS) {
if priority.is_some() {
return Err(Error::ParseError("More than one priority element in a presence."));
return Err(Error::ParseError(
"More than one priority element in a presence.",
));
}
check_no_attributes!(elem, "priority");
check_no_children!(elem, "priority");
@ -307,24 +321,37 @@ impl TryFrom<Element> for Presence {
impl From<Presence> for Element {
fn from(presence: Presence) -> Element {
Element::builder("presence")
.ns(ns::DEFAULT_NS)
.attr("from", presence.from)
.attr("to", presence.to)
.attr("id", presence.id)
.attr("type", presence.type_)
.append(presence.show)
.append(presence.statuses.into_iter().map(|(lang, status)| {
Element::builder("status")
.attr("xml:lang", match lang.as_ref() {
"" => None,
lang => Some(lang),
})
.append(status)
.build()
}).collect::<Vec<_>>())
.append(if presence.priority == 0 { None } else { Some(format!("{}", presence.priority)) })
.append(presence.payloads)
.build()
.ns(ns::DEFAULT_NS)
.attr("from", presence.from)
.attr("to", presence.to)
.attr("id", presence.id)
.attr("type", presence.type_)
.append(presence.show)
.append(
presence
.statuses
.into_iter()
.map(|(lang, status)| {
Element::builder("status")
.attr(
"xml:lang",
match lang.as_ref() {
"" => None,
lang => Some(lang),
},
)
.append(status)
.build()
})
.collect::<Vec<_>>(),
)
.append(if presence.priority == 0 {
None
} else {
Some(format!("{}", presence.priority))
})
.append(presence.payloads)
.build()
}
}
@ -354,7 +381,9 @@ mod tests {
#[cfg(not(feature = "component"))]
let elem: Element = "<presence xmlns='jabber:client'/>".parse().unwrap();
#[cfg(feature = "component")]
let elem: Element = "<presence xmlns='jabber:component:accept'/>".parse().unwrap();
let elem: Element = "<presence xmlns='jabber:component:accept'/>"
.parse()
.unwrap();
let presence = Presence::try_from(elem).unwrap();
assert_eq!(presence.from, None);
assert_eq!(presence.to, None);
@ -366,9 +395,13 @@ mod tests {
#[test]
fn test_serialise() {
#[cfg(not(feature = "component"))]
let elem: Element = "<presence xmlns='jabber:client' type='unavailable'/>/>".parse().unwrap();
let elem: Element = "<presence xmlns='jabber:client' type='unavailable'/>/>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<presence xmlns='jabber:component:accept' type='unavailable'/>/>".parse().unwrap();
let elem: Element = "<presence xmlns='jabber:component:accept' type='unavailable'/>/>"
.parse()
.unwrap();
let presence = Presence::new(Type::Unavailable);
let elem2 = presence.into();
assert!(elem.compare_to(&elem2));
@ -377,9 +410,14 @@ mod tests {
#[test]
fn test_show() {
#[cfg(not(feature = "component"))]
let elem: Element = "<presence xmlns='jabber:client'><show>chat</show></presence>".parse().unwrap();
let elem: Element = "<presence xmlns='jabber:client'><show>chat</show></presence>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<presence xmlns='jabber:component:accept'><show>chat</show></presence>".parse().unwrap();
let elem: Element =
"<presence xmlns='jabber:component:accept'><show>chat</show></presence>"
.parse()
.unwrap();
let presence = Presence::try_from(elem).unwrap();
assert_eq!(presence.payloads.len(), 0);
assert_eq!(presence.show, Show::Chat);
@ -388,9 +426,13 @@ mod tests {
#[test]
fn test_missing_show_value() {
#[cfg(not(feature = "component"))]
let elem: Element = "<presence xmlns='jabber:client'><show/></presence>".parse().unwrap();
let elem: Element = "<presence xmlns='jabber:client'><show/></presence>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<presence xmlns='jabber:component:accept'><show/></presence>".parse().unwrap();
let elem: Element = "<presence xmlns='jabber:component:accept'><show/></presence>"
.parse()
.unwrap();
let error = Presence::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -403,9 +445,14 @@ mod tests {
fn test_invalid_show() {
// "online" used to be a pretty common mistake.
#[cfg(not(feature = "component"))]
let elem: Element = "<presence xmlns='jabber:client'><show>online</show></presence>".parse().unwrap();
let elem: Element = "<presence xmlns='jabber:client'><show>online</show></presence>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<presence xmlns='jabber:component:accept'><show>online</show></presence>".parse().unwrap();
let elem: Element =
"<presence xmlns='jabber:component:accept'><show>online</show></presence>"
.parse()
.unwrap();
let error = Presence::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -417,9 +464,13 @@ mod tests {
#[test]
fn test_empty_status() {
#[cfg(not(feature = "component"))]
let elem: Element = "<presence xmlns='jabber:client'><status/></presence>".parse().unwrap();
let elem: Element = "<presence xmlns='jabber:client'><status/></presence>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<presence xmlns='jabber:component:accept'><status/></presence>".parse().unwrap();
let elem: Element = "<presence xmlns='jabber:component:accept'><status/></presence>"
.parse()
.unwrap();
let presence = Presence::try_from(elem).unwrap();
assert_eq!(presence.payloads.len(), 0);
assert_eq!(presence.statuses.len(), 1);
@ -429,9 +480,14 @@ mod tests {
#[test]
fn test_status() {
#[cfg(not(feature = "component"))]
let elem: Element = "<presence xmlns='jabber:client'><status>Here!</status></presence>".parse().unwrap();
let elem: Element = "<presence xmlns='jabber:client'><status>Here!</status></presence>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<presence xmlns='jabber:component:accept'><status>Here!</status></presence>".parse().unwrap();
let elem: Element =
"<presence xmlns='jabber:component:accept'><status>Here!</status></presence>"
.parse()
.unwrap();
let presence = Presence::try_from(elem).unwrap();
assert_eq!(presence.payloads.len(), 0);
assert_eq!(presence.statuses.len(), 1);
@ -462,15 +518,23 @@ mod tests {
Error::ParseError(string) => string,
_ => panic!(),
};
assert_eq!(message, "Status element present twice for the same xml:lang.");
assert_eq!(
message,
"Status element present twice for the same xml:lang."
);
}
#[test]
fn test_priority() {
#[cfg(not(feature = "component"))]
let elem: Element = "<presence xmlns='jabber:client'><priority>-1</priority></presence>".parse().unwrap();
let elem: Element = "<presence xmlns='jabber:client'><priority>-1</priority></presence>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<presence xmlns='jabber:component:accept'><priority>-1</priority></presence>".parse().unwrap();
let elem: Element =
"<presence xmlns='jabber:component:accept'><priority>-1</priority></presence>"
.parse()
.unwrap();
let presence = Presence::try_from(elem).unwrap();
assert_eq!(presence.payloads.len(), 0);
assert_eq!(presence.priority, -1i8);
@ -479,9 +543,14 @@ mod tests {
#[test]
fn test_invalid_priority() {
#[cfg(not(feature = "component"))]
let elem: Element = "<presence xmlns='jabber:client'><priority>128</priority></presence>".parse().unwrap();
let elem: Element = "<presence xmlns='jabber:client'><priority>128</priority></presence>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<presence xmlns='jabber:component:accept'><priority>128</priority></presence>".parse().unwrap();
let elem: Element =
"<presence xmlns='jabber:component:accept'><priority>128</priority></presence>"
.parse()
.unwrap();
let error = Presence::try_from(elem).unwrap_err();
match error {
Error::ParseIntError(_) => (),
@ -492,9 +561,14 @@ mod tests {
#[test]
fn test_unknown_child() {
#[cfg(not(feature = "component"))]
let elem: Element = "<presence xmlns='jabber:client'><test xmlns='invalid'/></presence>".parse().unwrap();
let elem: Element = "<presence xmlns='jabber:client'><test xmlns='invalid'/></presence>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<presence xmlns='jabber:component:accept'><test xmlns='invalid'/></presence>".parse().unwrap();
let elem: Element =
"<presence xmlns='jabber:component:accept'><test xmlns='invalid'/></presence>"
.parse()
.unwrap();
let presence = Presence::try_from(elem).unwrap();
let payload = &presence.payloads[0];
assert!(payload.is("test", "invalid"));
@ -503,9 +577,14 @@ mod tests {
#[test]
fn test_invalid_status_child() {
#[cfg(not(feature = "component"))]
let elem: Element = "<presence xmlns='jabber:client'><status><coucou/></status></presence>".parse().unwrap();
let elem: Element = "<presence xmlns='jabber:client'><status><coucou/></status></presence>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<presence xmlns='jabber:component:accept'><status><coucou/></status></presence>".parse().unwrap();
let elem: Element =
"<presence xmlns='jabber:component:accept'><status><coucou/></status></presence>"
.parse()
.unwrap();
let error = Presence::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -517,9 +596,14 @@ mod tests {
#[test]
fn test_invalid_attribute() {
#[cfg(not(feature = "component"))]
let elem: Element = "<presence xmlns='jabber:client'><status coucou=''/></presence>".parse().unwrap();
let elem: Element = "<presence xmlns='jabber:client'><status coucou=''/></presence>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<presence xmlns='jabber:component:accept'><status coucou=''/></presence>".parse().unwrap();
let elem: Element =
"<presence xmlns='jabber:component:accept'><status coucou=''/></presence>"
.parse()
.unwrap();
let error = Presence::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,

View File

@ -4,19 +4,14 @@
// 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 try_from::TryFrom;
use minidom::Element;
use jid::Jid;
use crate::date::DateTime;
use crate::error::Error;
use crate::ns;
use crate::data_forms::DataForm;
use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId};
use crate::date::DateTime;
use crate::error::Error;
use crate::ns;
use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId};
use jid::Jid;
use minidom::Element;
use try_from::TryFrom;
/// One PubSub item from a node.
#[derive(Debug, Clone)]
@ -40,7 +35,9 @@ impl TryFrom<Element> for Item {
let mut payloads = elem.children().cloned().collect::<Vec<_>>();
let payload = payloads.pop();
if !payloads.is_empty() {
return Err(Error::ParseError("More than a single payload in item element."));
return Err(Error::ParseError(
"More than a single payload in item element.",
));
}
Ok(Item {
payload,
@ -53,11 +50,11 @@ impl TryFrom<Element> for Item {
impl From<Item> for Element {
fn from(item: Item) -> Element {
Element::builder("item")
.ns(ns::PUBSUB_EVENT)
.attr("id", item.id)
.attr("publisher", item.publisher)
.append(item.payload)
.build()
.ns(ns::PUBSUB_EVENT)
.attr("id", item.id)
.attr("publisher", item.publisher)
.append(item.payload)
.build()
}
}
@ -131,21 +128,29 @@ pub enum PubSubEvent {
fn parse_items(elem: Element, node: NodeName) -> Result<PubSubEvent, Error> {
let mut is_retract = None;
let mut items = vec!();
let mut retracts = vec!();
let mut items = vec![];
let mut retracts = vec![];
for child in elem.children() {
if child.is("item", ns::PUBSUB_EVENT) {
match is_retract {
None => is_retract = Some(false),
Some(false) => (),
Some(true) => return Err(Error::ParseError("Mix of item and retract in items element.")),
Some(true) => {
return Err(Error::ParseError(
"Mix of item and retract in items element.",
))
}
}
items.push(Item::try_from(child.clone())?);
} else if child.is("retract", ns::PUBSUB_EVENT) {
match is_retract {
None => is_retract = Some(true),
Some(true) => (),
Some(false) => return Err(Error::ParseError("Mix of item and retract in items element.")),
Some(false) => {
return Err(Error::ParseError(
"Mix of item and retract in items element.",
))
}
}
check_no_children!(child, "retract");
check_no_unknown_attributes!(child, "retract", ["id"]);
@ -157,7 +162,10 @@ fn parse_items(elem: Element, node: NodeName) -> Result<PubSubEvent, Error> {
}
Ok(match is_retract {
Some(false) => PubSubEvent::PublishedItems { node, items },
Some(true) => PubSubEvent::RetractedItems { node, items: retracts },
Some(true) => PubSubEvent::RetractedItems {
node,
items: retracts,
},
None => return Err(Error::ParseError("Missing children in items element.")),
})
}
@ -176,7 +184,9 @@ impl TryFrom<Element> for PubSubEvent {
let mut payloads = child.children().cloned().collect::<Vec<_>>();
let item = payloads.pop();
if !payloads.is_empty() {
return Err(Error::ParseError("More than a single payload in configuration element."));
return Err(Error::ParseError(
"More than a single payload in configuration element.",
));
}
let form = match item {
None => None,
@ -188,7 +198,9 @@ impl TryFrom<Element> for PubSubEvent {
for item in child.children() {
if item.is("redirect", ns::PUBSUB_EVENT) {
if redirect.is_some() {
return Err(Error::ParseError("More than one redirect in delete element."));
return Err(Error::ParseError(
"More than one redirect in delete element.",
));
}
let uri = get_attr!(item, "uri", required);
redirect = Some(uri);
@ -222,77 +234,79 @@ impl TryFrom<Element> for PubSubEvent {
impl From<PubSubEvent> for Element {
fn from(event: PubSubEvent) -> Element {
let payload = match event {
PubSubEvent::Configuration { node, form } => {
Element::builder("configuration")
PubSubEvent::Configuration { node, form } => Element::builder("configuration")
.ns(ns::PUBSUB_EVENT)
.attr("node", node)
.append(form)
.build(),
PubSubEvent::Delete { node, redirect } => Element::builder("purge")
.ns(ns::PUBSUB_EVENT)
.attr("node", node)
.append(redirect.map(|redirect| {
Element::builder("redirect")
.ns(ns::PUBSUB_EVENT)
.attr("node", node)
.append(form)
.attr("uri", redirect)
.build()
},
PubSubEvent::Delete { node, redirect } => {
Element::builder("purge")
.ns(ns::PUBSUB_EVENT)
.attr("node", node)
.append(redirect.map(|redirect| {
Element::builder("redirect")
.ns(ns::PUBSUB_EVENT)
.attr("uri", redirect)
.build()
}))
.build()
},
PubSubEvent::PublishedItems { node, items } => {
Element::builder("items")
.ns(ns::PUBSUB_EVENT)
.attr("node", node)
.append(items)
.build()
},
PubSubEvent::RetractedItems { node, items } => {
Element::builder("items")
.ns(ns::PUBSUB_EVENT)
.attr("node", node)
.append(items.into_iter().map(|id| {
Element::builder("retract")
.ns(ns::PUBSUB_EVENT)
.attr("id", id)
.build()
}).collect::<Vec<_>>())
.build()
},
PubSubEvent::Purge { node } => {
Element::builder("purge")
.ns(ns::PUBSUB_EVENT)
.attr("node", node)
.build()
},
PubSubEvent::Subscription { node, expiry, jid, subid, subscription } => {
Element::builder("subscription")
.ns(ns::PUBSUB_EVENT)
.attr("node", node)
.attr("expiry", expiry)
.attr("jid", jid)
.attr("subid", subid)
.attr("subscription", subscription)
.build()
},
}))
.build(),
PubSubEvent::PublishedItems { node, items } => Element::builder("items")
.ns(ns::PUBSUB_EVENT)
.attr("node", node)
.append(items)
.build(),
PubSubEvent::RetractedItems { node, items } => Element::builder("items")
.ns(ns::PUBSUB_EVENT)
.attr("node", node)
.append(
items
.into_iter()
.map(|id| {
Element::builder("retract")
.ns(ns::PUBSUB_EVENT)
.attr("id", id)
.build()
})
.collect::<Vec<_>>(),
)
.build(),
PubSubEvent::Purge { node } => Element::builder("purge")
.ns(ns::PUBSUB_EVENT)
.attr("node", node)
.build(),
PubSubEvent::Subscription {
node,
expiry,
jid,
subid,
subscription,
} => Element::builder("subscription")
.ns(ns::PUBSUB_EVENT)
.attr("node", node)
.attr("expiry", expiry)
.attr("jid", jid)
.attr("subid", subid)
.attr("subscription", subscription)
.build(),
};
Element::builder("event")
.ns(ns::PUBSUB_EVENT)
.append(payload)
.build()
.ns(ns::PUBSUB_EVENT)
.append(payload)
.build()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
use crate::compare_elements::NamespaceAwareCompare;
use std::str::FromStr;
#[test]
fn missing_items() {
let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event'><items node='coucou'/></event>".parse().unwrap();
let elem: Element =
"<event xmlns='http://jabber.org/protocol/pubsub#event'><items node='coucou'/></event>"
.parse()
.unwrap();
let error = PubSubEvent::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -309,9 +323,12 @@ mod tests {
PubSubEvent::PublishedItems { node, items } => {
assert_eq!(node, NodeName(String::from("coucou")));
assert_eq!(items[0].id, Some(ItemId(String::from("test"))));
assert_eq!(items[0].publisher, Some(Jid::from_str("test@coucou").unwrap()));
assert_eq!(
items[0].publisher,
Some(Jid::from_str("test@coucou").unwrap())
);
assert_eq!(items[0].payload, None);
},
}
_ => panic!(),
}
}
@ -329,7 +346,7 @@ mod tests {
Some(ref elem) => assert!(elem.is("foreign", "example:namespace")),
_ => panic!(),
}
},
}
_ => panic!(),
}
}
@ -343,7 +360,7 @@ mod tests {
assert_eq!(node, NodeName(String::from("something")));
assert_eq!(items[0], ItemId(String::from("coucou")));
assert_eq!(items[1], ItemId(String::from("test")));
},
}
_ => panic!(),
}
}
@ -356,19 +373,22 @@ mod tests {
PubSubEvent::Delete { node, redirect } => {
assert_eq!(node, NodeName(String::from("coucou")));
assert_eq!(redirect, Some(String::from("hello")));
},
}
_ => panic!(),
}
}
#[test]
fn test_simple_purge() {
let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event'><purge node='coucou'/></event>".parse().unwrap();
let elem: Element =
"<event xmlns='http://jabber.org/protocol/pubsub#event'><purge node='coucou'/></event>"
.parse()
.unwrap();
let event = PubSubEvent::try_from(elem).unwrap();
match event {
PubSubEvent::Purge { node } => {
assert_eq!(node, NodeName(String::from("coucou")));
},
}
_ => panic!(),
}
}
@ -381,14 +401,17 @@ mod tests {
PubSubEvent::Configuration { node, form: _ } => {
assert_eq!(node, NodeName(String::from("coucou")));
//assert_eq!(form.type_, Result_);
},
}
_ => panic!(),
}
}
#[test]
fn test_invalid() {
let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event'><coucou node='test'/></event>".parse().unwrap();
let elem: Element =
"<event xmlns='http://jabber.org/protocol/pubsub#event'><coucou node='test'/></event>"
.parse()
.unwrap();
let error = PubSubEvent::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -399,7 +422,9 @@ mod tests {
#[test]
fn test_invalid_attribute() {
let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event' coucou=''/>".parse().unwrap();
let elem: Element = "<event xmlns='http://jabber.org/protocol/pubsub#event' coucou=''/>"
.parse()
.unwrap();
let error = PubSubEvent::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -419,16 +444,29 @@ mod tests {
subid='ba49252aaa4f5d320c24d3766f0bdcade78c78d3'
subscription='subscribed'/>
</event>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let event = PubSubEvent::try_from(elem.clone()).unwrap();
match event.clone() {
PubSubEvent::Subscription { node, expiry, jid, subid, subscription } => {
PubSubEvent::Subscription {
node,
expiry,
jid,
subid,
subscription,
} => {
assert_eq!(node, NodeName(String::from("princely_musings")));
assert_eq!(subid, Some(SubscriptionId(String::from("ba49252aaa4f5d320c24d3766f0bdcade78c78d3"))));
assert_eq!(
subid,
Some(SubscriptionId(String::from(
"ba49252aaa4f5d320c24d3766f0bdcade78c78d3"
)))
);
assert_eq!(subscription, Some(Subscription::Subscribed));
assert_eq!(jid, Some(Jid::from_str("francisco@denmark.lit").unwrap()));
assert_eq!(expiry, Some("2006-02-28T23:59:59Z".parse().unwrap()));
},
}
_ => panic!(),
}

View File

@ -4,19 +4,14 @@
// 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 try_from::TryFrom;
use minidom::Element;
use jid::Jid;
use crate::error::Error;
use crate::ns;
use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload};
use crate::data_forms::DataForm;
use crate::pubsub::{NodeName, ItemId, Subscription, SubscriptionId};
use crate::error::Error;
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
use crate::ns;
use crate::pubsub::{ItemId, NodeName, Subscription, SubscriptionId};
use jid::Jid;
use minidom::Element;
use try_from::TryFrom;
// TODO: a better solution would be to split this into a query and a result elements, like for
// XEP-0030.
@ -137,7 +132,9 @@ impl TryFrom<Element> for Item {
let mut payloads = elem.children().cloned().collect::<Vec<_>>();
let payload = payloads.pop();
if !payloads.is_empty() {
return Err(Error::ParseError("More than a single payload in item element."));
return Err(Error::ParseError(
"More than a single payload in item element.",
));
}
Ok(Item {
payload,
@ -149,10 +146,10 @@ impl TryFrom<Element> for Item {
impl From<Item> for Element {
fn from(item: Item) -> Element {
Element::builder("item")
.ns(ns::PUBSUB)
.attr("id", item.id)
.append(item.payload)
.build()
.ns(ns::PUBSUB)
.attr("id", item.id)
.append(item.payload)
.build()
}
}
@ -199,7 +196,9 @@ generate_element!(
generate_attribute!(
/// Whether a retract request should notify subscribers or not.
Notify, "notify", bool
Notify,
"notify",
bool
);
generate_element!(
@ -235,11 +234,15 @@ impl TryFrom<Element> for SubscribeOptions {
for child in elem.children() {
if child.is("required", ns::PUBSUB) {
if required {
return Err(Error::ParseError("More than one required element in subscribe-options."));
return Err(Error::ParseError(
"More than one required element in subscribe-options.",
));
}
required = true;
} else {
return Err(Error::ParseError("Unknown child in subscribe-options element."));
return Err(Error::ParseError(
"Unknown child in subscribe-options element.",
));
}
}
Ok(SubscribeOptions { required })
@ -251,12 +254,10 @@ impl From<SubscribeOptions> for Element {
Element::builder("subscribe-options")
.ns(ns::PUBSUB)
.append(if subscribe_options.required {
vec!(Element::builder("required")
.ns(ns::PUBSUB)
.build())
} else {
vec!()
})
vec![Element::builder("required").ns(ns::PUBSUB).build()]
} else {
vec![]
})
.build()
}
}
@ -334,7 +335,7 @@ pub enum PubSub {
create: Create,
/// The configure request for the new node.
configure: Option<Configure>
configure: Option<Configure>,
},
/// Request to publish items to a node, with optional options.
@ -343,7 +344,7 @@ pub enum PubSub {
publish: Publish,
/// The options related to this publish request.
publish_options: Option<PublishOptions>
publish_options: Option<PublishOptions>,
},
/// A list of affiliations you have on a service, or on a node.
@ -383,75 +384,114 @@ impl TryFrom<Element> for PubSub {
for child in elem.children() {
if child.is("create", ns::PUBSUB) {
if payload.is_some() {
return Err(Error::ParseError("Payload is already defined in pubsub element."));
return Err(Error::ParseError(
"Payload is already defined in pubsub element.",
));
}
let create = Create::try_from(child.clone())?;
payload = Some(PubSub::Create { create, configure: None });
payload = Some(PubSub::Create {
create,
configure: None,
});
} else if child.is("configure", ns::PUBSUB) {
if let Some(PubSub::Create { create, configure }) = payload {
if configure.is_some() {
return Err(Error::ParseError("Configure is already defined in pubsub element."));
return Err(Error::ParseError(
"Configure is already defined in pubsub element.",
));
}
let configure = Some(Configure::try_from(child.clone())?);
payload = Some(PubSub::Create { create, configure });
} else {
return Err(Error::ParseError("Payload is already defined in pubsub element."));
return Err(Error::ParseError(
"Payload is already defined in pubsub element.",
));
}
} else if child.is("publish", ns::PUBSUB) {
if payload.is_some() {
return Err(Error::ParseError("Payload is already defined in pubsub element."));
return Err(Error::ParseError(
"Payload is already defined in pubsub element.",
));
}
let publish = Publish::try_from(child.clone())?;
payload = Some(PubSub::Publish { publish, publish_options: None });
payload = Some(PubSub::Publish {
publish,
publish_options: None,
});
} else if child.is("publish-options", ns::PUBSUB) {
if let Some(PubSub::Publish { publish, publish_options }) = payload {
if let Some(PubSub::Publish {
publish,
publish_options,
}) = payload
{
if publish_options.is_some() {
return Err(Error::ParseError("Publish-options are already defined in pubsub element."));
return Err(Error::ParseError(
"Publish-options are already defined in pubsub element.",
));
}
let publish_options = Some(PublishOptions::try_from(child.clone())?);
payload = Some(PubSub::Publish { publish, publish_options });
payload = Some(PubSub::Publish {
publish,
publish_options,
});
} else {
return Err(Error::ParseError("Payload is already defined in pubsub element."));
return Err(Error::ParseError(
"Payload is already defined in pubsub element.",
));
}
} else if child.is("affiliations", ns::PUBSUB) {
if payload.is_some() {
return Err(Error::ParseError("Payload is already defined in pubsub element."));
return Err(Error::ParseError(
"Payload is already defined in pubsub element.",
));
}
let affiliations = Affiliations::try_from(child.clone())?;
payload = Some(PubSub::Affiliations(affiliations));
} else if child.is("default", ns::PUBSUB) {
if payload.is_some() {
return Err(Error::ParseError("Payload is already defined in pubsub element."));
return Err(Error::ParseError(
"Payload is already defined in pubsub element.",
));
}
let default = Default::try_from(child.clone())?;
payload = Some(PubSub::Default(default));
} else if child.is("items", ns::PUBSUB) {
if payload.is_some() {
return Err(Error::ParseError("Payload is already defined in pubsub element."));
return Err(Error::ParseError(
"Payload is already defined in pubsub element.",
));
}
let items = Items::try_from(child.clone())?;
payload = Some(PubSub::Items(items));
} else if child.is("retract", ns::PUBSUB) {
if payload.is_some() {
return Err(Error::ParseError("Payload is already defined in pubsub element."));
return Err(Error::ParseError(
"Payload is already defined in pubsub element.",
));
}
let retract = Retract::try_from(child.clone())?;
payload = Some(PubSub::Retract(retract));
} else if child.is("subscription", ns::PUBSUB) {
if payload.is_some() {
return Err(Error::ParseError("Payload is already defined in pubsub element."));
return Err(Error::ParseError(
"Payload is already defined in pubsub element.",
));
}
let subscription = SubscriptionElem::try_from(child.clone())?;
payload = Some(PubSub::Subscription(subscription));
} else if child.is("subscriptions", ns::PUBSUB) {
if payload.is_some() {
return Err(Error::ParseError("Payload is already defined in pubsub element."));
return Err(Error::ParseError(
"Payload is already defined in pubsub element.",
));
}
let subscriptions = Subscriptions::try_from(child.clone())?;
payload = Some(PubSub::Subscriptions(subscriptions));
} else if child.is("unsubscribe", ns::PUBSUB) {
if payload.is_some() {
return Err(Error::ParseError("Payload is already defined in pubsub element."));
return Err(Error::ParseError(
"Payload is already defined in pubsub element.",
));
}
let unsubscribe = Unsubscribe::try_from(child.clone())?;
payload = Some(PubSub::Unsubscribe(unsubscribe));
@ -468,28 +508,31 @@ impl From<PubSub> for Element {
Element::builder("pubsub")
.ns(ns::PUBSUB)
.append(match pubsub {
PubSub::Create { create, configure } => {
let mut elems = vec!(Element::from(create));
if let Some(configure) = configure {
elems.push(Element::from(configure));
}
elems
},
PubSub::Publish { publish, publish_options } => {
let mut elems = vec!(Element::from(publish));
if let Some(publish_options) = publish_options {
elems.push(Element::from(publish_options));
}
elems
},
PubSub::Affiliations(affiliations) => vec!(Element::from(affiliations)),
PubSub::Default(default) => vec!(Element::from(default)),
PubSub::Items(items) => vec!(Element::from(items)),
PubSub::Retract(retract) => vec!(Element::from(retract)),
PubSub::Subscription(subscription) => vec!(Element::from(subscription)),
PubSub::Subscriptions(subscriptions) => vec!(Element::from(subscriptions)),
PubSub::Unsubscribe(unsubscribe) => vec!(Element::from(unsubscribe)),
})
PubSub::Create { create, configure } => {
let mut elems = vec![Element::from(create)];
if let Some(configure) = configure {
elems.push(Element::from(configure));
}
elems
}
PubSub::Publish {
publish,
publish_options,
} => {
let mut elems = vec![Element::from(publish)];
if let Some(publish_options) = publish_options {
elems.push(Element::from(publish_options));
}
elems
}
PubSub::Affiliations(affiliations) => vec![Element::from(affiliations)],
PubSub::Default(default) => vec![Element::from(default)],
PubSub::Items(items) => vec![Element::from(items)],
PubSub::Retract(retract) => vec![Element::from(retract)],
PubSub::Subscription(subscription) => vec![Element::from(subscription)],
PubSub::Subscriptions(subscriptions) => vec![Element::from(subscriptions)],
PubSub::Unsubscribe(unsubscribe) => vec![Element::from(unsubscribe)],
})
.build()
}
}
@ -501,7 +544,9 @@ mod tests {
#[test]
fn create() {
let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub'><create/></pubsub>".parse().unwrap();
let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub'><create/></pubsub>"
.parse()
.unwrap();
let elem1 = elem.clone();
let pubsub = PubSub::try_from(elem).unwrap();
match pubsub.clone() {
@ -515,7 +560,10 @@ mod tests {
let elem2 = Element::from(pubsub);
assert!(elem1.compare_to(&elem2));
let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub'><create node='coucou'/></pubsub>".parse().unwrap();
let elem: Element =
"<pubsub xmlns='http://jabber.org/protocol/pubsub'><create node='coucou'/></pubsub>"
.parse()
.unwrap();
let elem1 = elem.clone();
let pubsub = PubSub::try_from(elem).unwrap();
match pubsub.clone() {
@ -532,7 +580,10 @@ mod tests {
#[test]
fn create_and_configure() {
let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub'><create/><configure/></pubsub>".parse().unwrap();
let elem: Element =
"<pubsub xmlns='http://jabber.org/protocol/pubsub'><create/><configure/></pubsub>"
.parse()
.unwrap();
let elem1 = elem.clone();
let pubsub = PubSub::try_from(elem).unwrap();
match pubsub.clone() {
@ -549,11 +600,17 @@ mod tests {
#[test]
fn publish() {
let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub'><publish node='coucou'/></pubsub>".parse().unwrap();
let elem: Element =
"<pubsub xmlns='http://jabber.org/protocol/pubsub'><publish node='coucou'/></pubsub>"
.parse()
.unwrap();
let elem1 = elem.clone();
let pubsub = PubSub::try_from(elem).unwrap();
match pubsub.clone() {
PubSub::Publish { publish, publish_options } => {
PubSub::Publish {
publish,
publish_options,
} => {
assert_eq!(&publish.node.0, "coucou");
assert!(publish_options.is_none());
}
@ -570,7 +627,10 @@ mod tests {
let elem1 = elem.clone();
let pubsub = PubSub::try_from(elem).unwrap();
match pubsub.clone() {
PubSub::Publish { publish, publish_options } => {
PubSub::Publish {
publish,
publish_options,
} => {
assert_eq!(&publish.node.0, "coucou");
assert!(publish_options.unwrap().form.is_none());
}
@ -583,7 +643,9 @@ mod tests {
#[test]
fn invalid_empty_pubsub() {
let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub'/>".parse().unwrap();
let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub'/>"
.parse()
.unwrap();
let error = PubSub::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -596,12 +658,17 @@ mod tests {
fn publish_option() {
let elem: Element = "<publish-options xmlns='http://jabber.org/protocol/pubsub'><x xmlns='jabber:x:data' type='submit'><field var='FORM_TYPE' type='hidden'><value>http://jabber.org/protocol/pubsub#publish-options</value></field></x></publish-options>".parse().unwrap();
let publish_options = PublishOptions::try_from(elem).unwrap();
assert_eq!(&publish_options.form.unwrap().form_type.unwrap(), "http://jabber.org/protocol/pubsub#publish-options");
assert_eq!(
&publish_options.form.unwrap().form_type.unwrap(),
"http://jabber.org/protocol/pubsub#publish-options"
);
}
#[test]
fn subscribe_options() {
let elem1: Element = "<subscribe-options xmlns='http://jabber.org/protocol/pubsub'/>".parse().unwrap();
let elem1: Element = "<subscribe-options xmlns='http://jabber.org/protocol/pubsub'/>"
.parse()
.unwrap();
let subscribe_options1 = SubscribeOptions::try_from(elem1).unwrap();
assert_eq!(subscribe_options1.required, false);

View File

@ -9,7 +9,9 @@ use crate::message::MessagePayload;
generate_empty_element!(
/// Requests that this message is acked by the final recipient once
/// received.
Request, "request", RECEIPTS
Request,
"request",
RECEIPTS
);
impl MessagePayload for Request {}
@ -29,9 +31,9 @@ impl MessagePayload for Received {}
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::ns;
use minidom::Element;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -55,7 +57,9 @@ mod tests {
let elem: Element = "<received xmlns='urn:xmpp:receipts'/>".parse().unwrap();
Received::try_from(elem).unwrap();
let elem: Element = "<received xmlns='urn:xmpp:receipts' id='coucou'/>".parse().unwrap();
let elem: Element = "<received xmlns='urn:xmpp:receipts' id='coucou'/>"
.parse()
.unwrap();
Received::try_from(elem).unwrap();
}

View File

@ -4,12 +4,14 @@
// 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 crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
use jid::Jid;
use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload};
generate_elem_id!(
/// Represents a group a contact is part of.
Group, "group", ROSTER
Group,
"group",
ROSTER
);
generate_attribute!(
@ -79,11 +81,11 @@ impl IqResultPayload for Roster {}
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use std::str::FromStr;
use crate::compare_elements::NamespaceAwareCompare;
use crate::error::Error;
use minidom::Element;
use std::str::FromStr;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -122,7 +124,9 @@ mod tests {
let roster2 = Roster::try_from(elem2).unwrap();
assert_eq!(roster.items, roster2.items);
let elem: Element = "<query xmlns='jabber:iq:roster' ver='ver9'/>".parse().unwrap();
let elem: Element = "<query xmlns='jabber:iq:roster' ver='ver9'/>"
.parse()
.unwrap();
let roster = Roster::try_from(elem).unwrap();
assert_eq!(roster.ver, Some(String::from("ver9")));
assert!(roster.items.is_empty());
@ -141,14 +145,22 @@ mod tests {
name='Benvolio'
subscription='both'/>
</query>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let roster = Roster::try_from(elem).unwrap();
assert_eq!(roster.ver, Some(String::from("ver11")));
assert_eq!(roster.items.len(), 3);
assert_eq!(roster.items[0].jid, Jid::from_str("romeo@example.net").unwrap());
assert_eq!(
roster.items[0].jid,
Jid::from_str("romeo@example.net").unwrap()
);
assert_eq!(roster.items[0].name, Some(String::from("Romeo")));
assert_eq!(roster.items[0].subscription, Subscription::Both);
assert_eq!(roster.items[0].groups, vec!(Group::from_str("Friends").unwrap()));
assert_eq!(
roster.items[0].groups,
vec!(Group::from_str("Friends").unwrap())
);
}
#[test]
@ -160,12 +172,17 @@ mod tests {
<group>B</group>
</item>
</query>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let elem1 = elem.clone();
let roster = Roster::try_from(elem).unwrap();
assert!(roster.ver.is_none());
assert_eq!(roster.items.len(), 1);
assert_eq!(roster.items[0].jid, Jid::from_str("test@example.org").unwrap());
assert_eq!(
roster.items[0].jid,
Jid::from_str("test@example.org").unwrap()
);
assert_eq!(roster.items[0].name, None);
assert_eq!(roster.items[0].groups.len(), 2);
assert_eq!(roster.items[0].groups[0], Group::from_str("A").unwrap());
@ -176,7 +193,10 @@ mod tests {
#[test]
fn test_set() {
let elem: Element = "<query xmlns='jabber:iq:roster'><item jid='nurse@example.com'/></query>".parse().unwrap();
let elem: Element =
"<query xmlns='jabber:iq:roster'><item jid='nurse@example.com'/></query>"
.parse()
.unwrap();
let roster = Roster::try_from(elem).unwrap();
assert!(roster.ver.is_none());
assert_eq!(roster.items.len(), 1);
@ -188,25 +208,38 @@ mod tests {
<group>Servants</group>
</item>
</query>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let roster = Roster::try_from(elem).unwrap();
assert!(roster.ver.is_none());
assert_eq!(roster.items.len(), 1);
assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap());
assert_eq!(
roster.items[0].jid,
Jid::from_str("nurse@example.com").unwrap()
);
assert_eq!(roster.items[0].name, Some(String::from("Nurse")));
assert_eq!(roster.items[0].groups.len(), 1);
assert_eq!(roster.items[0].groups[0], Group::from_str("Servants").unwrap());
assert_eq!(
roster.items[0].groups[0],
Group::from_str("Servants").unwrap()
);
let elem: Element = r#"
<query xmlns='jabber:iq:roster'>
<item jid='nurse@example.com'
subscription='remove'/>
</query>
"#.parse().unwrap();
"#
.parse()
.unwrap();
let roster = Roster::try_from(elem).unwrap();
assert!(roster.ver.is_none());
assert_eq!(roster.items.len(), 1);
assert_eq!(roster.items[0].jid, Jid::from_str("nurse@example.com").unwrap());
assert_eq!(
roster.items[0].jid,
Jid::from_str("nurse@example.com").unwrap()
);
assert!(roster.items[0].name.is_none());
assert!(roster.items[0].groups.is_empty());
assert_eq!(roster.items[0].subscription, Subscription::Remove);
@ -214,7 +247,9 @@ mod tests {
#[test]
fn test_invalid() {
let elem: Element = "<query xmlns='jabber:iq:roster'><coucou/></query>".parse().unwrap();
let elem: Element = "<query xmlns='jabber:iq:roster'><coucou/></query>"
.parse()
.unwrap();
let error = Roster::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -222,7 +257,9 @@ mod tests {
};
assert_eq!(message, "Unknown child in query element.");
let elem: Element = "<query xmlns='jabber:iq:roster' coucou=''/>".parse().unwrap();
let elem: Element = "<query xmlns='jabber:iq:roster' coucou=''/>"
.parse()
.unwrap();
let error = Roster::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -233,7 +270,9 @@ mod tests {
#[test]
fn test_invalid_item() {
let elem: Element = "<query xmlns='jabber:iq:roster'><item/></query>".parse().unwrap();
let elem: Element = "<query xmlns='jabber:iq:roster'><item/></query>"
.parse()
.unwrap();
let error = Roster::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -251,7 +290,10 @@ mod tests {
assert_eq!(error.description(), "Invalid JID, I guess?");
*/
let elem: Element = "<query xmlns='jabber:iq:roster'><item jid='coucou'><coucou/></item></query>".parse().unwrap();
let elem: Element =
"<query xmlns='jabber:iq:roster'><item jid='coucou'><coucou/></item></query>"
.parse()
.unwrap();
let error = Roster::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,

View File

@ -74,12 +74,30 @@ impl TryFrom<Element> for SetQuery {
impl From<SetQuery> for Element {
fn from(set: SetQuery) -> Element {
Element::builder("set")
.ns(ns::RSM)
.append(set.max.map(|max| Element::builder("max").ns(ns::RSM).append(format!("{}", max)).build()))
.append(set.after.map(|after| Element::builder("after").ns(ns::RSM).append(after).build()))
.append(set.before.map(|before| Element::builder("before").ns(ns::RSM).append(before).build()))
.append(set.index.map(|index| Element::builder("index").ns(ns::RSM).append(format!("{}", index)).build()))
.build()
.ns(ns::RSM)
.append(set.max.map(|max| {
Element::builder("max")
.ns(ns::RSM)
.append(format!("{}", max))
.build()
}))
.append(
set.after
.map(|after| Element::builder("after").ns(ns::RSM).append(after).build()),
)
.append(set.before.map(|before| {
Element::builder("before")
.ns(ns::RSM)
.append(before)
.build()
}))
.append(set.index.map(|index| {
Element::builder("index")
.ns(ns::RSM)
.append(format!("{}", index))
.build()
}))
.build()
}
}
@ -138,18 +156,27 @@ impl TryFrom<Element> for SetResult {
impl From<SetResult> for Element {
fn from(set: SetResult) -> Element {
let first = set.first.clone()
.map(|first| Element::builder("first")
.ns(ns::RSM)
.attr("index", set.first_index)
.append(first)
.build());
Element::builder("set")
let first = set.first.clone().map(|first| {
Element::builder("first")
.ns(ns::RSM)
.attr("index", set.first_index)
.append(first)
.append(set.last.map(|last| Element::builder("last").ns(ns::RSM).append(last).build()))
.append(set.count.map(|count| Element::builder("count").ns(ns::RSM).append(format!("{}", count)).build()))
.build()
});
Element::builder("set")
.ns(ns::RSM)
.append(first)
.append(
set.last
.map(|last| Element::builder("last").ns(ns::RSM).append(last).build()),
)
.append(set.count.map(|count| {
Element::builder("count")
.ns(ns::RSM)
.append(format!("{}", count))
.build()
}))
.build()
}
}
@ -174,14 +201,18 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>".parse().unwrap();
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
.parse()
.unwrap();
let set = SetQuery::try_from(elem).unwrap();
assert_eq!(set.max, None);
assert_eq!(set.after, None);
assert_eq!(set.before, None);
assert_eq!(set.index, None);
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>".parse().unwrap();
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
.parse()
.unwrap();
let set = SetResult::try_from(elem).unwrap();
match set.first {
Some(_) => panic!(),
@ -193,7 +224,9 @@ mod tests {
#[test]
fn test_unknown() {
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>".parse().unwrap();
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
.parse()
.unwrap();
let error = SetQuery::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -201,7 +234,9 @@ mod tests {
};
assert_eq!(message, "This is not a RSM set element.");
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>".parse().unwrap();
let elem: Element = "<replace xmlns='urn:xmpp:message-correct:0'/>"
.parse()
.unwrap();
let error = SetResult::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -212,7 +247,9 @@ mod tests {
#[test]
fn test_invalid_child() {
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>".parse().unwrap();
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>"
.parse()
.unwrap();
let error = SetQuery::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -220,7 +257,9 @@ mod tests {
};
assert_eq!(message, "Unknown child in set element.");
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>".parse().unwrap();
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><coucou/></set>"
.parse()
.unwrap();
let error = SetResult::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -231,7 +270,9 @@ mod tests {
#[test]
fn test_serialise() {
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>".parse().unwrap();
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
.parse()
.unwrap();
let rsm = SetQuery {
max: None,
after: None,
@ -241,7 +282,9 @@ mod tests {
let elem2 = rsm.into();
assert_eq!(elem, elem2);
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>".parse().unwrap();
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'/>"
.parse()
.unwrap();
let rsm = SetResult {
first: None,
first_index: None,
@ -254,7 +297,10 @@ mod tests {
#[test]
fn test_first_index() {
let elem: Element = "<set xmlns='http://jabber.org/protocol/rsm'><first index='4'>coucou</first></set>".parse().unwrap();
let elem: Element =
"<set xmlns='http://jabber.org/protocol/rsm'><first index='4'>coucou</first></set>"
.parse()
.unwrap();
let elem1 = elem.clone();
let set = SetResult::try_from(elem).unwrap();
assert_eq!(set.first, Some(String::from("coucou")));

View File

@ -6,10 +6,10 @@
use std::collections::BTreeMap;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use crate::ns;
use minidom::Element;
use try_from::TryFrom;
use crate::helpers::Base64;
@ -84,7 +84,9 @@ generate_element!(
generate_empty_element!(
/// Sent by the client at any point after [auth](struct.Auth.html) if it
/// wants to cancel the current authentication process.
Abort, "abort", SASL
Abort,
"abort",
SASL
);
generate_element!(
@ -166,11 +168,15 @@ impl TryFrom<Element> for Failure {
check_no_children!(child, "text");
let lang = get_attr!(child, "xml:lang", default);
if texts.insert(lang, child.text()).is_some() {
return Err(Error::ParseError("Text element present twice for the same xml:lang in failure element."));
return Err(Error::ParseError(
"Text element present twice for the same xml:lang in failure element.",
));
}
} else if child.has_ns(ns::SASL) {
if defined_condition.is_some() {
return Err(Error::ParseError("Failure must not have more than one defined-condition."));
return Err(Error::ParseError(
"Failure must not have more than one defined-condition.",
));
}
check_no_attributes!(child, "defined-condition");
check_no_children!(child, "defined-condition");
@ -184,7 +190,8 @@ impl TryFrom<Element> for Failure {
return Err(Error::ParseError("Unknown element in Failure."));
}
}
let defined_condition = defined_condition.ok_or(Error::ParseError("Failure must have a defined-condition."))?;
let defined_condition =
defined_condition.ok_or(Error::ParseError("Failure must have a defined-condition."))?;
Ok(Failure {
defined_condition: defined_condition,
@ -196,24 +203,30 @@ impl TryFrom<Element> for Failure {
impl From<Failure> for Element {
fn from(failure: Failure) -> Element {
Element::builder("failure")
.ns(ns::SASL)
.append(failure.defined_condition)
.append(failure.texts.into_iter().map(|(lang, text)| {
Element::builder("text")
.ns(ns::SASL)
.attr("xml:lang", lang)
.append(text)
.build()
}).collect::<Vec<_>>())
.build()
.ns(ns::SASL)
.append(failure.defined_condition)
.append(
failure
.texts
.into_iter()
.map(|(lang, text)| {
Element::builder("text")
.ns(ns::SASL)
.attr("xml:lang", lang)
.append(text)
.build()
})
.collect::<Vec<_>>(),
)
.build()
}
}
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -243,7 +256,9 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'/>".parse().unwrap();
let elem: Element = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'/>"
.parse()
.unwrap();
let auth = Auth::try_from(elem).unwrap();
assert_eq!(auth.mechanism, Mechanism::Plain);
assert!(auth.data.is_empty());
@ -251,7 +266,10 @@ mod tests {
#[test]
fn section_6_5_1() {
let elem: Element = "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><aborted/></failure>".parse().unwrap();
let elem: Element =
"<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><aborted/></failure>"
.parse()
.unwrap();
let failure = Failure::try_from(elem).unwrap();
assert_eq!(failure.defined_condition, DefinedCondition::Aborted);
assert!(failure.texts.is_empty());
@ -262,9 +280,14 @@ mod tests {
let elem: Element = "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<account-disabled/>
<text xml:lang='en'>Call 212-555-1212 for assistance.</text>
</failure>".parse().unwrap();
</failure>"
.parse()
.unwrap();
let failure = Failure::try_from(elem).unwrap();
assert_eq!(failure.defined_condition, DefinedCondition::AccountDisabled);
assert_eq!(failure.texts["en"], String::from("Call 212-555-1212 for assistance."));
assert_eq!(
failure.texts["en"],
String::from("Call 212-555-1212 for assistance.")
);
}
}

View File

@ -24,7 +24,9 @@ impl A {
generate_attribute!(
/// Whether to allow resumption of a previous stream.
ResumeAttr, "resume", bool
ResumeAttr,
"resume",
bool
);
generate_element!(
@ -103,7 +105,9 @@ generate_element!(
generate_empty_element!(
/// Requests the currently received stanzas by the other party.
R, "r", SM
R,
"r",
SM
);
generate_element!(
@ -135,14 +139,16 @@ generate_element!(
// TODO: add support for optional and required.
generate_empty_element!(
/// Represents availability of Stream Management in `<stream:features/>`.
StreamManagement, "sm", SM
StreamManagement,
"sm",
SM
);
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -189,12 +195,16 @@ mod tests {
#[test]
fn resume() {
let elem: Element = "<enable xmlns='urn:xmpp:sm:3' resume='true'/>".parse().unwrap();
let elem: Element = "<enable xmlns='urn:xmpp:sm:3' resume='true'/>"
.parse()
.unwrap();
let enable = Enable::try_from(elem).unwrap();
assert_eq!(enable.max, None);
assert_eq!(enable.resume, ResumeAttr::True);
let elem: Element = "<enabled xmlns='urn:xmpp:sm:3' resume='true' id='coucou' max='600'/>".parse().unwrap();
let elem: Element = "<enabled xmlns='urn:xmpp:sm:3' resume='true' id='coucou' max='600'/>"
.parse()
.unwrap();
let enabled = Enabled::try_from(elem).unwrap();
let previd = enabled.id.unwrap();
assert_eq!(enabled.resume, ResumeAttr::True);
@ -202,12 +212,16 @@ mod tests {
assert_eq!(enabled.max, Some(600));
assert_eq!(enabled.location, None);
let elem: Element = "<resume xmlns='urn:xmpp:sm:3' h='5' previd='coucou'/>".parse().unwrap();
let elem: Element = "<resume xmlns='urn:xmpp:sm:3' h='5' previd='coucou'/>"
.parse()
.unwrap();
let resume = Resume::try_from(elem).unwrap();
assert_eq!(resume.h, 5);
assert_eq!(resume.previd, previd);
let elem: Element = "<resumed xmlns='urn:xmpp:sm:3' h='5' previd='coucou'/>".parse().unwrap();
let elem: Element = "<resumed xmlns='urn:xmpp:sm:3' h='5' previd='coucou'/>"
.parse()
.unwrap();
let resumed = Resumed::try_from(elem).unwrap();
assert_eq!(resumed.h, 5);
assert_eq!(resumed.previd, previd);

View File

@ -4,16 +4,16 @@
// 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 try_from::TryFrom;
use std::collections::BTreeMap;
use try_from::TryFrom;
use minidom::Element;
use crate::message::MessagePayload;
use crate::presence::PresencePayload;
use crate::error::Error;
use jid::Jid;
use crate::message::MessagePayload;
use crate::ns;
use crate::presence::PresencePayload;
use jid::Jid;
generate_attribute!(
/// The type of the error.
@ -234,23 +234,30 @@ impl TryFrom<Element> for StanzaError {
check_no_children!(child, "text");
let lang = get_attr!(elem, "xml:lang", default);
if texts.insert(lang, child.text()).is_some() {
return Err(Error::ParseError("Text element present twice for the same xml:lang."));
return Err(Error::ParseError(
"Text element present twice for the same xml:lang.",
));
}
} else if child.has_ns(ns::XMPP_STANZAS) {
if defined_condition.is_some() {
return Err(Error::ParseError("Error must not have more than one defined-condition."));
return Err(Error::ParseError(
"Error must not have more than one defined-condition.",
));
}
check_no_children!(child, "defined-condition");
let condition = DefinedCondition::try_from(child.clone())?;
defined_condition = Some(condition);
} else {
if other.is_some() {
return Err(Error::ParseError("Error must not have more than one other element."));
return Err(Error::ParseError(
"Error must not have more than one other element.",
));
}
other = Some(child.clone());
}
}
let defined_condition = defined_condition.ok_or(Error::ParseError("Error must have a defined-condition."))?;
let defined_condition =
defined_condition.ok_or(Error::ParseError("Error must have a defined-condition."))?;
Ok(StanzaError {
type_: type_,
@ -265,17 +272,17 @@ impl TryFrom<Element> for StanzaError {
impl From<StanzaError> for Element {
fn from(err: StanzaError) -> Element {
let mut root = Element::builder("error")
.ns(ns::DEFAULT_NS)
.attr("type", err.type_)
.attr("by", err.by)
.append(err.defined_condition)
.build();
.ns(ns::DEFAULT_NS)
.attr("type", err.type_)
.attr("by", err.by)
.append(err.defined_condition)
.build();
for (lang, text) in err.texts {
let elem = Element::builder("text")
.ns(ns::XMPP_STANZAS)
.attr("xml:lang", lang)
.append(text)
.build();
.ns(ns::XMPP_STANZAS)
.attr("xml:lang", lang)
.append(text)
.build();
root.append_child(elem);
}
if let Some(other) = err.other {
@ -313,7 +320,10 @@ mod tests {
let elem: Element = "<error xmlns='jabber:component:accept' type='cancel'><undefined-condition xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error>".parse().unwrap();
let error = StanzaError::try_from(elem).unwrap();
assert_eq!(error.type_, ErrorType::Cancel);
assert_eq!(error.defined_condition, DefinedCondition::UndefinedCondition);
assert_eq!(
error.defined_condition,
DefinedCondition::UndefinedCondition
);
}
#[test]
@ -330,9 +340,13 @@ mod tests {
assert_eq!(message, "Required attribute 'type' missing.");
#[cfg(not(feature = "component"))]
let elem: Element = "<error xmlns='jabber:client' type='coucou'/>".parse().unwrap();
let elem: Element = "<error xmlns='jabber:client' type='coucou'/>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<error xmlns='jabber:component:accept' type='coucou'/>".parse().unwrap();
let elem: Element = "<error xmlns='jabber:component:accept' type='coucou'/>"
.parse()
.unwrap();
let error = StanzaError::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -344,9 +358,13 @@ mod tests {
#[test]
fn test_invalid_condition() {
#[cfg(not(feature = "component"))]
let elem: Element = "<error xmlns='jabber:client' type='cancel'/>".parse().unwrap();
let elem: Element = "<error xmlns='jabber:client' type='cancel'/>"
.parse()
.unwrap();
#[cfg(feature = "component")]
let elem: Element = "<error xmlns='jabber:component:accept' type='cancel'/>".parse().unwrap();
let elem: Element = "<error xmlns='jabber:component:accept' type='cancel'/>"
.parse()
.unwrap();
let error = StanzaError::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,

View File

@ -37,10 +37,10 @@ impl MessagePayload for OriginId {}
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::error::Error;
use minidom::Element;
use std::str::FromStr;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -58,19 +58,25 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou' by='coucou@coucou'/>".parse().unwrap();
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou' by='coucou@coucou'/>"
.parse()
.unwrap();
let stanza_id = StanzaId::try_from(elem).unwrap();
assert_eq!(stanza_id.id, String::from("coucou"));
assert_eq!(stanza_id.by, Jid::from_str("coucou@coucou").unwrap());
let elem: Element = "<origin-id xmlns='urn:xmpp:sid:0' id='coucou'/>".parse().unwrap();
let elem: Element = "<origin-id xmlns='urn:xmpp:sid:0' id='coucou'/>"
.parse()
.unwrap();
let origin_id = OriginId::try_from(elem).unwrap();
assert_eq!(origin_id.id, String::from("coucou"));
}
#[test]
fn test_invalid_child() {
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0'><coucou/></stanza-id>".parse().unwrap();
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0'><coucou/></stanza-id>"
.parse()
.unwrap();
let error = StanzaId::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -92,7 +98,9 @@ mod tests {
#[test]
fn test_invalid_by() {
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou'/>".parse().unwrap();
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou'/>"
.parse()
.unwrap();
let error = StanzaId::try_from(elem).unwrap_err();
let message = match error {
Error::ParseError(string) => string,
@ -103,8 +111,13 @@ mod tests {
#[test]
fn test_serialise() {
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou' by='coucou@coucou'/>".parse().unwrap();
let stanza_id = StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() };
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0' id='coucou' by='coucou@coucou'/>"
.parse()
.unwrap();
let stanza_id = StanzaId {
id: String::from("coucou"),
by: Jid::from_str("coucou@coucou").unwrap(),
};
let elem2 = stanza_id.into();
assert_eq!(elem, elem2);
}

View File

@ -73,8 +73,8 @@ impl Stream {
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]

View File

@ -11,7 +11,9 @@ generate_empty_element!(
///
/// It should only be used in an `<iq type='get'/>`, as it can only
/// represent the request, and not a result.
VersionQuery, "query", VERSION
VersionQuery,
"query",
VERSION
);
impl IqGetPayload for VersionQuery {}
@ -39,9 +41,9 @@ impl IqResultPayload for VersionResult {}
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use crate::compare_elements::NamespaceAwareCompare;
use minidom::Element;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -59,7 +61,10 @@ mod tests {
#[test]
fn simple() {
let elem: Element = "<query xmlns='jabber:iq:version'><name>xmpp-rs</name><version>0.3.0</version></query>".parse().unwrap();
let elem: Element =
"<query xmlns='jabber:iq:version'><name>xmpp-rs</name><version>0.3.0</version></query>"
.parse()
.unwrap();
let version = VersionResult::try_from(elem).unwrap();
assert_eq!(version.name, String::from("xmpp-rs"));
assert_eq!(version.version, String::from("0.3.0"));
@ -74,7 +79,10 @@ mod tests {
os: None,
};
let elem1 = Element::from(version);
let elem2: Element = "<query xmlns='jabber:iq:version'><name>xmpp-rs</name><version>0.3.0</version></query>".parse().unwrap();
let elem2: Element =
"<query xmlns='jabber:iq:version'><name>xmpp-rs</name><version>0.3.0</version></query>"
.parse()
.unwrap();
println!("{:?}", elem1);
assert!(elem1.compare_to(&elem2));
}

View File

@ -72,8 +72,8 @@ impl Open {
#[cfg(test)]
mod tests {
use super::*;
use try_from::TryFrom;
use minidom::Element;
use try_from::TryFrom;
#[cfg(target_pointer_width = "32")]
#[test]
@ -89,7 +89,9 @@ mod tests {
#[test]
fn test_simple() {
let elem: Element = "<open xmlns='urn:ietf:params:xml:ns:xmpp-framing'/>".parse().unwrap();
let elem: Element = "<open xmlns='urn:ietf:params:xml:ns:xmpp-framing'/>"
.parse()
.unwrap();
let open = Open::try_from(elem).unwrap();
assert_eq!(open.from, None);
assert_eq!(open.to, None);