parsers: use Error type from xso

This is a large change and as such, it needs good motivation. Let me
remind you of the ultimate goal: we want a derive macro which allows us
to FromXml/IntoXml, and that derive macro should be usable from
`xmpp_parsers` and other crates.

For that, any code generated by the derive macro mustn't depend on any
code in the `xmpp_parsers` crate, because you cannot name the crate you
are in portably (`xmpp_parsers::..` wouldn't resolve within
`xmpp_parsers`, and `crate::..` would point at other crates if the macro
was used in other crates).

We also want to interoperate with code already implementing
`TryFrom<Element>` and `Into<Element>` on structs. This ultimately
requires that we have an error type which is shared by the two
implementations and that error type must be declared in the `xso` crate
to be usable by the macros.

Thus, we port the error type over to use the type declared in `xso`.

This changes the structure of the error type greatly; I do not think
that `xso` should have to know about all the different types we are
parsing there and they don't deserve special treatment. Wrapping them in
a `Box<dyn ..>` seems more appropriate.
This commit is contained in:
Jonas Schäfer 2024-06-21 16:27:43 +02:00
commit 6ef8dbefa3
62 changed files with 916 additions and 892 deletions

View file

@ -18,9 +18,9 @@ impl MessagePayload for Attention {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[cfg(not(feature = "disable-validation"))]
use crate::util::error::Error;
use crate::Element; use crate::Element;
#[cfg(not(feature = "disable-validation"))]
use xso::error::{Error, FromElementError};
#[test] #[test]
fn test_size() { fn test_size() {
@ -41,7 +41,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Attention::try_from(elem).unwrap_err(); let error = Attention::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in attention element."); assert_eq!(message, "Unknown child in attention element.");
@ -55,7 +55,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Attention::try_from(elem).unwrap_err(); let error = Attention::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in attention element."); assert_eq!(message, "Unknown attribute in attention element.");

View file

@ -58,9 +58,9 @@ impl PubSubPayload for Data {}
mod tests { mod tests {
use super::*; use super::*;
use crate::hashes::Algo; use crate::hashes::Algo;
#[cfg(not(feature = "disable-validation"))]
use crate::util::error::Error;
use crate::Element; use crate::Element;
#[cfg(not(feature = "disable-validation"))]
use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -119,7 +119,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Data::try_from(elem).unwrap_err(); let error = Data::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in data element.") assert_eq!(message, "Unknown attribute in data element.")

View file

@ -6,10 +6,10 @@
use crate::iq::{IqResultPayload, IqSetPayload}; use crate::iq::{IqResultPayload, IqSetPayload};
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use jid::{FullJid, Jid}; use jid::{FullJid, Jid};
use std::str::FromStr; use std::str::FromStr;
use xso::error::{Error, FromElementError};
/// The request for resource binding, which is the process by which a client /// 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. /// can obtain a full JID and start exchanging on the XMPP network.
@ -34,23 +34,23 @@ impl BindQuery {
impl IqSetPayload for BindQuery {} impl IqSetPayload for BindQuery {}
impl TryFrom<Element> for BindQuery { impl TryFrom<Element> for BindQuery {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<BindQuery, Error> { fn try_from(elem: Element) -> Result<BindQuery, FromElementError> {
check_self!(elem, "bind", BIND); check_self!(elem, "bind", BIND);
check_no_attributes!(elem, "bind"); check_no_attributes!(elem, "bind");
let mut resource = None; let mut resource = None;
for child in elem.children() { for child in elem.children() {
if resource.is_some() { if resource.is_some() {
return Err(Error::ParseError("Bind can only have one child.")); return Err(Error::Other("Bind can only have one child.").into());
} }
if child.is("resource", ns::BIND) { if child.is("resource", ns::BIND) {
check_no_attributes!(child, "resource"); check_no_attributes!(child, "resource");
check_no_children!(child, "resource"); check_no_children!(child, "resource");
resource = Some(child.text()); resource = Some(child.text());
} else { } else {
return Err(Error::ParseError("Unknown element in bind request.")); return Err(Error::Other("Unknown element in bind request.").into());
} }
} }
@ -93,32 +93,30 @@ impl From<BindResponse> for Jid {
} }
impl TryFrom<Element> for BindResponse { impl TryFrom<Element> for BindResponse {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<BindResponse, Error> { fn try_from(elem: Element) -> Result<BindResponse, FromElementError> {
check_self!(elem, "bind", BIND); check_self!(elem, "bind", BIND);
check_no_attributes!(elem, "bind"); check_no_attributes!(elem, "bind");
let mut jid = None; let mut jid = None;
for child in elem.children() { for child in elem.children() {
if jid.is_some() { if jid.is_some() {
return Err(Error::ParseError("Bind can only have one child.")); return Err(Error::Other("Bind can only have one child.").into());
} }
if child.is("jid", ns::BIND) { if child.is("jid", ns::BIND) {
check_no_attributes!(child, "jid"); check_no_attributes!(child, "jid");
check_no_children!(child, "jid"); check_no_children!(child, "jid");
jid = Some(FullJid::from_str(&child.text())?); jid = Some(FullJid::from_str(&child.text()).map_err(Error::text_parse_error)?);
} else { } else {
return Err(Error::ParseError("Unknown element in bind response.")); return Err(Error::Other("Unknown element in bind response.").into());
} }
} }
Ok(BindResponse { Ok(BindResponse {
jid: match jid { jid: match jid {
None => { None => {
return Err(Error::ParseError( return Err(Error::Other("Bind response must contain a jid element.").into())
"Bind response must contain a jid element.",
))
} }
Some(jid) => jid, Some(jid) => jid,
}, },
@ -187,7 +185,7 @@ mod tests {
.unwrap(); .unwrap();
let error = BindQuery::try_from(elem).unwrap_err(); let error = BindQuery::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in resource element."); assert_eq!(message, "Unknown attribute in resource element.");
@ -197,7 +195,7 @@ mod tests {
.unwrap(); .unwrap();
let error = BindQuery::try_from(elem).unwrap_err(); let error = BindQuery::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in resource element."); assert_eq!(message, "Unknown child in resource element.");

View file

@ -6,9 +6,9 @@
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use jid::Jid; use jid::Jid;
use xso::error::FromElementError;
generate_empty_element!( generate_empty_element!(
/// The element requesting the blocklist, the result iq will contain a /// The element requesting the blocklist, the result iq will contain a
@ -30,9 +30,9 @@ macro_rules! generate_blocking_element {
} }
impl TryFrom<Element> for $elem { impl TryFrom<Element> for $elem {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<$elem, Error> { fn try_from(elem: Element) -> Result<$elem, FromElementError> {
check_self!(elem, $name, BLOCKING); check_self!(elem, $name, BLOCKING);
check_no_attributes!(elem, $name); check_no_attributes!(elem, $name);
let mut items = vec!(); let mut items = vec!();
@ -96,6 +96,8 @@ generate_empty_element!(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use xso::error::Error;
use super::*; use super::*;
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
@ -165,7 +167,7 @@ mod tests {
let request_elem = elem.clone(); let request_elem = elem.clone();
let error = BlocklistRequest::try_from(request_elem).unwrap_err(); let error = BlocklistRequest::try_from(request_elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in blocklist element."); assert_eq!(message, "Unknown attribute in blocklist element.");
@ -173,7 +175,7 @@ mod tests {
let result_elem = elem.clone(); let result_elem = elem.clone();
let error = BlocklistResult::try_from(result_elem).unwrap_err(); let error = BlocklistResult::try_from(result_elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in blocklist element."); assert_eq!(message, "Unknown attribute in blocklist element.");
@ -183,7 +185,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Block::try_from(elem).unwrap_err(); let error = Block::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in block element."); assert_eq!(message, "Unknown attribute in block element.");
@ -193,7 +195,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Unblock::try_from(elem).unwrap_err(); let error = Unblock::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in unblock element."); assert_eq!(message, "Unknown attribute in unblock element.");
@ -205,7 +207,7 @@ mod tests {
let elem: Element = "<blocklist xmlns='urn:xmpp:blocking'><item jid='coucou@coucou'/><item jid='domain'/></blocklist>".parse().unwrap(); let elem: Element = "<blocklist xmlns='urn:xmpp:blocking'><item jid='coucou@coucou'/><item jid='domain'/></blocklist>".parse().unwrap();
let error = BlocklistRequest::try_from(elem).unwrap_err(); let error = BlocklistRequest::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in blocklist element."); assert_eq!(message, "Unknown child in blocklist element.");

View file

@ -5,10 +5,10 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::hashes::{Algo, Hash}; use crate::hashes::{Algo, Hash};
use crate::util::error::Error;
use crate::util::text_node_codecs::{Base64, Codec}; use crate::util::text_node_codecs::{Base64, Codec};
use minidom::IntoAttributeValue; use minidom::IntoAttributeValue;
use std::str::FromStr; use std::str::FromStr;
use xso::error::Error;
/// A Content-ID, as defined in RFC2111. /// A Content-ID, as defined in RFC2111.
/// ///
@ -27,11 +27,11 @@ impl FromStr for ContentId {
let temp: Vec<_> = match temp[..] { let temp: Vec<_> = match temp[..] {
[lhs, rhs] => { [lhs, rhs] => {
if rhs != "bob.xmpp.org" { if rhs != "bob.xmpp.org" {
return Err(Error::ParseError("Wrong domain for cid URI.")); return Err(Error::Other("Wrong domain for cid URI."));
} }
lhs.splitn(2, '+').collect() lhs.splitn(2, '+').collect()
} }
_ => return Err(Error::ParseError("Missing @ in cid URI.")), _ => return Err(Error::Other("Missing @ in cid URI.")),
}; };
let (algo, hex) = match temp[..] { let (algo, hex) = match temp[..] {
[lhs, rhs] => { [lhs, rhs] => {
@ -42,9 +42,9 @@ impl FromStr for ContentId {
}; };
(algo, rhs) (algo, rhs)
} }
_ => return Err(Error::ParseError("Missing + in cid URI.")), _ => return Err(Error::Other("Missing + in cid URI.")),
}; };
let hash = Hash::from_hex(algo, hex)?; let hash = Hash::from_hex(algo, hex).map_err(Error::text_parse_error)?;
Ok(ContentId { hash }) Ok(ContentId { hash })
} }
} }
@ -89,6 +89,7 @@ generate_element!(
mod tests { mod tests {
use super::*; use super::*;
use crate::Element; use crate::Element;
use xso::error::FromElementError;
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -135,14 +136,14 @@ mod tests {
fn invalid_cid() { fn invalid_cid() {
let error = "Hello world!".parse::<ContentId>().unwrap_err(); let error = "Hello world!".parse::<ContentId>().unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, Error::Other(string) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Missing @ in cid URI."); assert_eq!(message, "Missing @ in cid URI.");
let error = "Hello world@bob.xmpp.org".parse::<ContentId>().unwrap_err(); let error = "Hello world@bob.xmpp.org".parse::<ContentId>().unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, Error::Other(string) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Missing + in cid URI."); assert_eq!(message, "Missing + in cid URI.");
@ -151,7 +152,7 @@ mod tests {
.parse::<ContentId>() .parse::<ContentId>()
.unwrap_err(); .unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, Error::Other(string) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Wrong domain for cid URI."); assert_eq!(message, "Wrong domain for cid URI.");
@ -160,7 +161,7 @@ mod tests {
.parse::<ContentId>() .parse::<ContentId>()
.unwrap_err(); .unwrap_err();
let message = match error { let message = match error {
Error::ParseIntError(error) => error, Error::TextParseError(error) if error.is::<std::num::ParseIntError>() => error,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message.to_string(), "invalid digit found in string"); assert_eq!(message.to_string(), "invalid digit found in string");
@ -173,7 +174,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Data::try_from(elem).unwrap_err(); let error = Data::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in data element."); assert_eq!(message, "Unknown child in data element.");

View file

@ -15,8 +15,8 @@
//! This module exposes the [`Autojoin`][crate::bookmarks2::Autojoin] boolean flag, the [`Conference`][crate::bookmarks2::Conference] chatroom element, and the [BOOKMARKS2][crate::ns::BOOKMARKS2] XML namespace. //! This module exposes the [`Autojoin`][crate::bookmarks2::Autojoin] boolean flag, the [`Conference`][crate::bookmarks2::Conference] chatroom element, and the [BOOKMARKS2][crate::ns::BOOKMARKS2] XML namespace.
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
generate_attribute!( generate_attribute!(
/// Whether a conference bookmark should be joined automatically. /// Whether a conference bookmark should be joined automatically.
@ -52,9 +52,9 @@ impl Conference {
} }
impl TryFrom<Element> for Conference { impl TryFrom<Element> for Conference {
type Error = Error; type Error = FromElementError;
fn try_from(root: Element) -> Result<Conference, Error> { fn try_from(root: Element) -> Result<Conference, FromElementError> {
check_self!(root, "conference", BOOKMARKS2, "Conference"); check_self!(root, "conference", BOOKMARKS2, "Conference");
check_no_unknown_attributes!(root, "Conference", ["autojoin", "name"]); check_no_unknown_attributes!(root, "Conference", ["autojoin", "name"]);
@ -69,33 +69,30 @@ impl TryFrom<Element> for Conference {
for child in root.children() { for child in root.children() {
if child.is("nick", ns::BOOKMARKS2) { if child.is("nick", ns::BOOKMARKS2) {
if conference.nick.is_some() { if conference.nick.is_some() {
return Err(Error::ParseError( return Err(Error::Other("Conference must not have more than one nick.").into());
"Conference must not have more than one nick.",
));
} }
check_no_children!(child, "nick"); check_no_children!(child, "nick");
check_no_attributes!(child, "nick"); check_no_attributes!(child, "nick");
conference.nick = Some(child.text()); conference.nick = Some(child.text());
} else if child.is("password", ns::BOOKMARKS2) { } else if child.is("password", ns::BOOKMARKS2) {
if conference.password.is_some() { if conference.password.is_some() {
return Err(Error::ParseError( return Err(
"Conference must not have more than one password.", Error::Other("Conference must not have more than one password.").into(),
)); );
} }
check_no_children!(child, "password"); check_no_children!(child, "password");
check_no_attributes!(child, "password"); check_no_attributes!(child, "password");
conference.password = Some(child.text()); conference.password = Some(child.text());
} else if child.is("extensions", ns::BOOKMARKS2) { } else if child.is("extensions", ns::BOOKMARKS2) {
if !conference.extensions.is_empty() { if !conference.extensions.is_empty() {
return Err(Error::ParseError( return Err(Error::Other(
"Conference must not have more than one extensions element.", "Conference must not have more than one extensions element.",
)); )
.into());
} }
conference.extensions.extend(child.children().cloned()); conference.extensions.extend(child.children().cloned());
} else { } else {
return Err(Error::ParseError( return Err(Error::Other("Unknown element in bookmarks2 conference").into());
"Unknown element in bookmarks2 conference",
));
} }
} }

View file

@ -9,7 +9,6 @@ use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity};
use crate::hashes::{Algo, Hash}; use crate::hashes::{Algo, Hash};
use crate::ns; use crate::ns;
use crate::presence::PresencePayload; use crate::presence::PresencePayload;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use base64::{engine::general_purpose::STANDARD as Base64, Engine}; use base64::{engine::general_purpose::STANDARD as Base64, Engine};
use blake2::Blake2bVar; use blake2::Blake2bVar;
@ -17,6 +16,7 @@ use digest::{Digest, Update, VariableOutput};
use sha1::Sha1; use sha1::Sha1;
use sha2::{Sha256, Sha512}; use sha2::{Sha256, Sha512};
use sha3::{Sha3_256, Sha3_512}; use sha3::{Sha3_256, Sha3_512};
use xso::error::{Error, FromElementError};
/// Represents a capability hash for a given client. /// Represents a capability hash for a given client.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -39,16 +39,16 @@ pub struct Caps {
impl PresencePayload for Caps {} impl PresencePayload for Caps {}
impl TryFrom<Element> for Caps { impl TryFrom<Element> for Caps {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<Caps, Error> { fn try_from(elem: Element) -> Result<Caps, FromElementError> {
check_self!(elem, "c", CAPS, "caps"); check_self!(elem, "c", CAPS, "caps");
check_no_children!(elem, "caps"); check_no_children!(elem, "caps");
check_no_unknown_attributes!(elem, "caps", ["hash", "ver", "ext", "node"]); check_no_unknown_attributes!(elem, "caps", ["hash", "ver", "ext", "node"]);
let ver: String = get_attr!(elem, "ver", Required); let ver: String = get_attr!(elem, "ver", Required);
let hash = Hash { let hash = Hash {
algo: get_attr!(elem, "hash", Required), algo: get_attr!(elem, "hash", Required),
hash: Base64.decode(ver)?, hash: Base64.decode(ver).map_err(Error::text_parse_error)?,
}; };
Ok(Caps { Ok(Caps {
ext: get_attr!(elem, "ext", Option), ext: get_attr!(elem, "ext", Option),
@ -253,7 +253,7 @@ mod tests {
let elem: Element = "<c xmlns='http://jabber.org/protocol/caps'><hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=</hash></c>".parse().unwrap(); let elem: Element = "<c xmlns='http://jabber.org/protocol/caps'><hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=</hash></c>".parse().unwrap();
let error = Caps::try_from(elem).unwrap_err(); let error = Caps::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in caps element."); assert_eq!(message, "Unknown child in caps element.");

View file

@ -33,8 +33,8 @@ impl MessagePayload for ChatState {}
mod tests { mod tests {
use super::*; use super::*;
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
#[test] #[test]
fn test_size() { fn test_size() {
@ -56,7 +56,7 @@ mod tests {
.unwrap(); .unwrap();
let error = ChatState::try_from(elem).unwrap_err(); let error = ChatState::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "This is not a chatstate element."); assert_eq!(message, "This is not a chatstate element.");
@ -70,7 +70,7 @@ mod tests {
.unwrap(); .unwrap();
let error = ChatState::try_from(elem).unwrap_err(); let error = ChatState::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in chatstate element."); assert_eq!(message, "Unknown child in chatstate element.");
@ -84,7 +84,7 @@ mod tests {
.unwrap(); .unwrap();
let error = ChatState::try_from(elem).unwrap_err(); let error = ChatState::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in chatstate element."); assert_eq!(message, "Unknown attribute in chatstate element.");

View file

@ -6,8 +6,8 @@
use crate::media_element::MediaElement; use crate::media_element::MediaElement;
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
generate_element!( generate_element!(
/// Represents one of the possible values for a list- field. /// Represents one of the possible values for a list- field.
@ -168,9 +168,9 @@ impl Field {
} }
impl TryFrom<Element> for Field { impl TryFrom<Element> for Field {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<Field, Error> { fn try_from(elem: Element) -> Result<Field, FromElementError> {
check_self!(elem, "field", DATA_FORMS); check_self!(elem, "field", DATA_FORMS);
check_no_unknown_attributes!(elem, "field", ["label", "type", "var"]); check_no_unknown_attributes!(elem, "field", ["label", "type", "var"]);
let mut field = Field { let mut field = Field {
@ -185,7 +185,7 @@ impl TryFrom<Element> for Field {
}; };
if field.type_ != FieldType::Fixed && field.var.is_none() { if field.type_ != FieldType::Fixed && field.var.is_none() {
return Err(Error::ParseError("Required attribute 'var' missing.")); return Err(Error::Other("Required attribute 'var' missing.").into());
} }
for element in elem.children() { for element in elem.children() {
@ -195,14 +195,14 @@ impl TryFrom<Element> for Field {
field.values.push(element.text()); field.values.push(element.text());
} else if element.is("required", ns::DATA_FORMS) { } else if element.is("required", ns::DATA_FORMS) {
if field.required { if field.required {
return Err(Error::ParseError("More than one required element.")); return Err(Error::Other("More than one required element.").into());
} }
check_no_children!(element, "required"); check_no_children!(element, "required");
check_no_attributes!(element, "required"); check_no_attributes!(element, "required");
field.required = true; field.required = true;
} else if element.is("option", ns::DATA_FORMS) { } else if element.is("option", ns::DATA_FORMS) {
if !field.is_list() { if !field.is_list() {
return Err(Error::ParseError("Option element found in non-list field.")); return Err(Error::Other("Option element found in non-list field.").into());
} }
let option = Option_::try_from(element.clone())?; let option = Option_::try_from(element.clone())?;
field.options.push(option); field.options.push(option);
@ -214,9 +214,9 @@ impl TryFrom<Element> for Field {
check_no_attributes!(element, "desc"); check_no_attributes!(element, "desc");
field.desc = Some(element.text()); field.desc = Some(element.text());
} else { } else {
return Err(Error::ParseError( return Err(
"Field child isnt a value, option or media element.", Error::Other("Field child isnt a value, option or media element.").into(),
)); );
} }
} }
Ok(field) Ok(field)
@ -299,9 +299,9 @@ impl DataForm {
} }
impl TryFrom<Element> for DataForm { impl TryFrom<Element> for DataForm {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<DataForm, Error> { fn try_from(elem: Element) -> Result<DataForm, FromElementError> {
check_self!(elem, "x", DATA_FORMS); check_self!(elem, "x", DATA_FORMS);
check_no_unknown_attributes!(elem, "x", ["type"]); check_no_unknown_attributes!(elem, "x", ["type"]);
let type_ = get_attr!(elem, "type", Required); let type_ = get_attr!(elem, "type", Required);
@ -315,16 +315,14 @@ impl TryFrom<Element> for DataForm {
for child in elem.children() { for child in elem.children() {
if child.is("title", ns::DATA_FORMS) { if child.is("title", ns::DATA_FORMS) {
if form.title.is_some() { if form.title.is_some() {
return Err(Error::ParseError("More than one title in form element.")); return Err(Error::Other("More than one title in form element.").into());
} }
check_no_children!(child, "title"); check_no_children!(child, "title");
check_no_attributes!(child, "title"); check_no_attributes!(child, "title");
form.title = Some(child.text()); form.title = Some(child.text());
} else if child.is("instructions", ns::DATA_FORMS) { } else if child.is("instructions", ns::DATA_FORMS) {
if form.instructions.is_some() { if form.instructions.is_some() {
return Err(Error::ParseError( return Err(Error::Other("More than one instructions in form element.").into());
"More than one instructions in form element.",
));
} }
check_no_children!(child, "instructions"); check_no_children!(child, "instructions");
check_no_attributes!(child, "instructions"); check_no_attributes!(child, "instructions");
@ -334,17 +332,17 @@ impl TryFrom<Element> for DataForm {
if field.is_form_type(&form.type_) { if field.is_form_type(&form.type_) {
let mut field = field; let mut field = field;
if form.form_type.is_some() { if form.form_type.is_some() {
return Err(Error::ParseError("More than one FORM_TYPE in a data form.")); return Err(Error::Other("More than one FORM_TYPE in a data form.").into());
} }
if field.values.len() != 1 { if field.values.len() != 1 {
return Err(Error::ParseError("Wrong number of values in FORM_TYPE.")); return Err(Error::Other("Wrong number of values in FORM_TYPE.").into());
} }
form.form_type = field.values.pop(); form.form_type = field.values.pop();
} else { } else {
form.fields.push(field); form.fields.push(field);
} }
} else { } else {
return Err(Error::ParseError("Unknown child in data form element.")); return Err(Error::Other("Unknown child in data form element.").into());
} }
} }
Ok(form) Ok(form)
@ -415,7 +413,7 @@ mod tests {
.unwrap(); .unwrap();
let error = DataForm::try_from(elem).unwrap_err(); let error = DataForm::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'var' missing."); assert_eq!(message, "Required attribute 'var' missing.");
@ -474,7 +472,7 @@ mod tests {
let elem: Element = "<x xmlns='jabber:x:data'/>".parse().unwrap(); let elem: Element = "<x xmlns='jabber:x:data'/>".parse().unwrap();
let error = DataForm::try_from(elem).unwrap_err(); let error = DataForm::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'type' missing."); assert_eq!(message, "Required attribute 'type' missing.");
@ -482,10 +480,10 @@ mod tests {
let elem: Element = "<x xmlns='jabber:x:data' type='coucou'/>".parse().unwrap(); let elem: Element = "<x xmlns='jabber:x:data' type='coucou'/>".parse().unwrap();
let error = DataForm::try_from(elem).unwrap_err(); let error = DataForm::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string)) => string,
_ => panic!(), other => panic!("unexpected result: {:?}", other),
}; };
assert_eq!(message, "Unknown value for 'type' attribute."); assert_eq!(message.to_string(), "Unknown value for 'type' attribute.");
} }
#[test] #[test]
@ -495,7 +493,7 @@ mod tests {
.unwrap(); .unwrap();
let error = DataForm::try_from(elem).unwrap_err(); let error = DataForm::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in data form element."); assert_eq!(message, "Unknown child in data form element.");
@ -516,7 +514,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Option_::try_from(elem).unwrap_err(); let error = Option_::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Missing child value in option element."); assert_eq!(message, "Missing child value in option element.");
@ -524,7 +522,7 @@ mod tests {
let elem: Element = "<option xmlns='jabber:x:data' label='Coucou!'><value>coucou</value><value>error</value></option>".parse().unwrap(); let elem: Element = "<option xmlns='jabber:x:data' label='Coucou!'><value>coucou</value><value>error</value></option>".parse().unwrap();
let error = Option_::try_from(elem).unwrap_err(); let error = Option_::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!( assert_eq!(

View file

@ -4,7 +4,6 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::util::error::Error;
use chrono::{DateTime as ChronoDateTime, FixedOffset}; use chrono::{DateTime as ChronoDateTime, FixedOffset};
use minidom::{IntoAttributeValue, Node}; use minidom::{IntoAttributeValue, Node};
use std::str::FromStr; use std::str::FromStr;
@ -33,9 +32,9 @@ impl DateTime {
} }
impl FromStr for DateTime { impl FromStr for DateTime {
type Err = Error; type Err = chrono::ParseError;
fn from_str(s: &str) -> Result<DateTime, Error> { fn from_str(s: &str) -> Result<DateTime, Self::Err> {
Ok(DateTime(ChronoDateTime::parse_from_rfc3339(s)?)) Ok(DateTime(ChronoDateTime::parse_from_rfc3339(s)?))
} }
} }
@ -80,51 +79,27 @@ mod tests {
fn test_invalid_date() { fn test_invalid_date() {
// There is no thirteenth month. // There is no thirteenth month.
let error = DateTime::from_str("2017-13-01T12:23:34Z").unwrap_err(); let error = DateTime::from_str("2017-13-01T12:23:34Z").unwrap_err();
let message = match error { assert_eq!(error.to_string(), "input is out of range");
Error::ChronoParseError(string) => string,
_ => panic!(),
};
assert_eq!(message.to_string(), "input is out of range");
// Timezone ≥24:00 arent allowed. // Timezone ≥24:00 arent allowed.
let error = DateTime::from_str("2017-05-27T12:11:02+25:00").unwrap_err(); let error = DateTime::from_str("2017-05-27T12:11:02+25:00").unwrap_err();
let message = match error { assert_eq!(error.to_string(), "input is out of range");
Error::ChronoParseError(string) => string,
_ => panic!(),
};
assert_eq!(message.to_string(), "input is out of range");
// Timezone without the : separator arent allowed. // Timezone without the : separator arent allowed.
let error = DateTime::from_str("2017-05-27T12:11:02+0100").unwrap_err(); let error = DateTime::from_str("2017-05-27T12:11:02+0100").unwrap_err();
let message = match error { assert_eq!(error.to_string(), "input contains invalid characters");
Error::ChronoParseError(string) => string,
_ => panic!(),
};
assert_eq!(message.to_string(), "input contains invalid characters");
// No seconds, error message could be improved. // No seconds, error message could be improved.
let error = DateTime::from_str("2017-05-27T12:11+01:00").unwrap_err(); let error = DateTime::from_str("2017-05-27T12:11+01:00").unwrap_err();
let message = match error { assert_eq!(error.to_string(), "input contains invalid characters");
Error::ChronoParseError(string) => string,
_ => panic!(),
};
assert_eq!(message.to_string(), "input contains invalid characters");
// TODO: maybe well want to support this one, as per XEP-0082 §4. // TODO: maybe well want to support this one, as per XEP-0082 §4.
let error = DateTime::from_str("20170527T12:11:02+01:00").unwrap_err(); let error = DateTime::from_str("20170527T12:11:02+01:00").unwrap_err();
let message = match error { assert_eq!(error.to_string(), "input contains invalid characters");
Error::ChronoParseError(string) => string,
_ => panic!(),
};
assert_eq!(message.to_string(), "input contains invalid characters");
// No timezone. // No timezone.
let error = DateTime::from_str("2017-05-27T12:11:02").unwrap_err(); let error = DateTime::from_str("2017-05-27T12:11:02").unwrap_err();
let message = match error { assert_eq!(error.to_string(), "premature end of input");
Error::ChronoParseError(string) => string,
_ => panic!(),
};
assert_eq!(message.to_string(), "premature end of input");
} }
#[test] #[test]

View file

@ -32,10 +32,10 @@ impl PresencePayload for Delay {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use jid::BareJid; use jid::BareJid;
use std::str::FromStr; use std::str::FromStr;
use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -71,7 +71,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Delay::try_from(elem.clone()).unwrap_err(); let error = Delay::try_from(elem.clone()).unwrap_err();
let returned_elem = match error { let returned_elem = match error {
Error::TypeMismatch(_, _, elem) => elem, FromElementError::Mismatch(elem) => elem,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(elem, returned_elem); assert_eq!(elem, returned_elem);
@ -84,7 +84,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Delay::try_from(elem).unwrap_err(); let error = Delay::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in delay element."); assert_eq!(message, "Unknown child in delay element.");

View file

@ -8,9 +8,9 @@ use crate::data_forms::{DataForm, DataFormType};
use crate::iq::{IqGetPayload, IqResultPayload}; use crate::iq::{IqGetPayload, IqResultPayload};
use crate::ns; use crate::ns;
use crate::rsm::{SetQuery, SetResult}; use crate::rsm::{SetQuery, SetResult};
use crate::util::error::Error;
use crate::Element; use crate::Element;
use jid::Jid; use jid::Jid;
use xso::error::{Error, FromElementError};
generate_element!( generate_element!(
/// Structure representing a `<query xmlns='http://jabber.org/protocol/disco#info'/>` element. /// Structure representing a `<query xmlns='http://jabber.org/protocol/disco#info'/>` element.
@ -115,9 +115,9 @@ pub struct DiscoInfoResult {
impl IqResultPayload for DiscoInfoResult {} impl IqResultPayload for DiscoInfoResult {}
impl TryFrom<Element> for DiscoInfoResult { impl TryFrom<Element> for DiscoInfoResult {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<DiscoInfoResult, Error> { fn try_from(elem: Element) -> Result<DiscoInfoResult, FromElementError> {
check_self!(elem, "query", DISCO_INFO, "disco#info result"); check_self!(elem, "query", DISCO_INFO, "disco#info result");
check_no_unknown_attributes!(elem, "disco#info result", ["node"]); check_no_unknown_attributes!(elem, "disco#info result", ["node"]);
@ -138,30 +138,30 @@ impl TryFrom<Element> for DiscoInfoResult {
} else if child.is("x", ns::DATA_FORMS) { } else if child.is("x", ns::DATA_FORMS) {
let data_form = DataForm::try_from(child.clone())?; let data_form = DataForm::try_from(child.clone())?;
if data_form.type_ != DataFormType::Result_ { if data_form.type_ != DataFormType::Result_ {
return Err(Error::ParseError( return Err(
"Data form must have a 'result' type in disco#info.", Error::Other("Data form must have a 'result' type in disco#info.").into(),
)); );
} }
if data_form.form_type.is_none() { if data_form.form_type.is_none() {
return Err(Error::ParseError("Data form found without a FORM_TYPE.")); return Err(Error::Other("Data form found without a FORM_TYPE.").into());
} }
result.extensions.push(data_form); result.extensions.push(data_form);
} else { } else {
return Err(Error::ParseError("Unknown element in disco#info.")); return Err(Error::Other("Unknown element in disco#info.").into());
} }
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(not(feature = "disable-validation"))]
{ {
if result.identities.is_empty() { if result.identities.is_empty() {
return Err(Error::ParseError( return Err(
"There must be at least one identity in disco#info.", Error::Other("There must be at least one identity in disco#info.").into(),
)); );
} }
if result.features.is_empty() { if result.features.is_empty() {
return Err(Error::ParseError( return Err(
"There must be at least one feature in disco#info.", Error::Other("There must be at least one feature in disco#info.").into(),
)); );
} }
} }
@ -313,7 +313,7 @@ mod tests {
.unwrap(); .unwrap();
let error = DiscoInfoResult::try_from(elem).unwrap_err(); let error = DiscoInfoResult::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown element in disco#info."); assert_eq!(message, "Unknown element in disco#info.");
@ -327,7 +327,7 @@ mod tests {
.unwrap(); .unwrap();
let error = DiscoInfoResult::try_from(elem).unwrap_err(); let error = DiscoInfoResult::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'category' missing."); assert_eq!(message, "Required attribute 'category' missing.");
@ -338,7 +338,7 @@ mod tests {
.unwrap(); .unwrap();
let error = DiscoInfoResult::try_from(elem).unwrap_err(); let error = DiscoInfoResult::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'category' must not be empty."); assert_eq!(message, "Required attribute 'category' must not be empty.");
@ -346,7 +346,7 @@ mod tests {
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='coucou'/></query>".parse().unwrap(); 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(); let error = DiscoInfoResult::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'type' missing."); assert_eq!(message, "Required attribute 'type' missing.");
@ -354,7 +354,7 @@ mod tests {
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='coucou' type=''/></query>".parse().unwrap(); let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='coucou' type=''/></query>".parse().unwrap();
let error = DiscoInfoResult::try_from(elem).unwrap_err(); let error = DiscoInfoResult::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'type' must not be empty."); assert_eq!(message, "Required attribute 'type' must not be empty.");
@ -368,7 +368,7 @@ mod tests {
.unwrap(); .unwrap();
let error = DiscoInfoResult::try_from(elem).unwrap_err(); let error = DiscoInfoResult::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'var' missing."); assert_eq!(message, "Required attribute 'var' missing.");
@ -381,7 +381,7 @@ mod tests {
.unwrap(); .unwrap();
let error = DiscoInfoResult::try_from(elem).unwrap_err(); let error = DiscoInfoResult::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!( assert_eq!(
@ -392,7 +392,7 @@ mod tests {
let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='client' type='pc'/></query>".parse().unwrap(); 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(); let error = DiscoInfoResult::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "There must be at least one feature in disco#info."); assert_eq!(message, "There must be at least one feature in disco#info.");

View file

@ -9,12 +9,12 @@ use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity};
use crate::hashes::{Algo, Hash}; use crate::hashes::{Algo, Hash};
use crate::ns; use crate::ns;
use crate::presence::PresencePayload; use crate::presence::PresencePayload;
use crate::util::error::Error;
use base64::{engine::general_purpose::STANDARD as Base64, Engine}; use base64::{engine::general_purpose::STANDARD as Base64, Engine};
use blake2::Blake2bVar; use blake2::Blake2bVar;
use digest::{Digest, Update, VariableOutput}; use digest::{Digest, Update, VariableOutput};
use sha2::{Sha256, Sha512}; use sha2::{Sha256, Sha512};
use sha3::{Sha3_256, Sha3_512}; use sha3::{Sha3_256, Sha3_512};
use xso::error::Error;
generate_element!( generate_element!(
/// Represents a set of capability hashes, all of them must correspond to /// Represents a set of capability hashes, all of them must correspond to
@ -80,7 +80,7 @@ fn compute_identities(identities: &[Identity]) -> Vec<u8> {
fn compute_extensions(extensions: &[DataForm]) -> Result<Vec<u8>, Error> { fn compute_extensions(extensions: &[DataForm]) -> Result<Vec<u8>, Error> {
for extension in extensions { for extension in extensions {
if extension.form_type.is_none() { if extension.form_type.is_none() {
return Err(Error::ParseError("Missing FORM_TYPE in extension.")); return Err(Error::Other("Missing FORM_TYPE in extension."));
} }
} }
Ok(compute_items(extensions, 0x1c, |extension| { Ok(compute_items(extensions, 0x1c, |extension| {
@ -163,8 +163,8 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result<Hash, Error> {
hasher.finalize_variable(&mut vec).unwrap(); hasher.finalize_variable(&mut vec).unwrap();
vec vec
} }
Algo::Sha_1 => return Err(Error::ParseError("Disabled algorithm sha-1: unsafe.")), Algo::Sha_1 => return Err(Error::Other("Disabled algorithm sha-1: unsafe.").into()),
Algo::Unknown(_algo) => return Err(Error::ParseError("Unknown algorithm in ecaps2.")), Algo::Unknown(_algo) => return Err(Error::Other("Unknown algorithm in ecaps2.").into()),
}, },
algo, algo,
}) })
@ -187,6 +187,7 @@ pub fn query_ecaps2(hash: Hash) -> DiscoInfoQuery {
mod tests { mod tests {
use super::*; use super::*;
use crate::Element; use crate::Element;
use xso::error::FromElementError;
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -226,7 +227,7 @@ mod tests {
let elem: Element = "<c xmlns='urn:xmpp:caps'><hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=</hash><hash xmlns='urn:xmpp:hashes:1' algo='sha3-256'>+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=</hash></c>".parse().unwrap(); let elem: Element = "<c xmlns='urn:xmpp:caps'><hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=</hash><hash xmlns='urn:xmpp:hashes:1' algo='sha3-256'>+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=</hash></c>".parse().unwrap();
let error = ECaps2::try_from(elem).unwrap_err(); let error = ECaps2::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in c element."); assert_eq!(message, "Unknown child in c element.");

View file

@ -24,8 +24,8 @@ impl MessagePayload for ExplicitMessageEncryption {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -61,7 +61,7 @@ mod tests {
.unwrap(); .unwrap();
let error = ExplicitMessageEncryption::try_from(elem.clone()).unwrap_err(); let error = ExplicitMessageEncryption::try_from(elem.clone()).unwrap_err();
let returned_elem = match error { let returned_elem = match error {
Error::TypeMismatch(_, _, elem) => elem, FromElementError::Mismatch(elem) => elem,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(elem, returned_elem); assert_eq!(elem, returned_elem);
@ -74,7 +74,7 @@ mod tests {
.unwrap(); .unwrap();
let error = ExplicitMessageEncryption::try_from(elem).unwrap_err(); let error = ExplicitMessageEncryption::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in encryption element."); assert_eq!(message, "Unknown child in encryption element.");

View file

@ -26,8 +26,8 @@ generate_element!(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -54,7 +54,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Forwarded::try_from(elem).unwrap_err(); let error = Forwarded::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in forwarded element."); assert_eq!(message, "Unknown child in forwarded element.");

View file

@ -4,13 +4,13 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::util::error::Error;
use crate::util::text_node_codecs::{Base64, Codec}; use crate::util::text_node_codecs::{Base64, Codec};
use base64::{engine::general_purpose::STANDARD as Base64Engine, Engine}; use base64::{engine::general_purpose::STANDARD as Base64Engine, Engine};
use minidom::IntoAttributeValue; use minidom::IntoAttributeValue;
use std::num::ParseIntError; use std::num::ParseIntError;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::str::FromStr; use std::str::FromStr;
use xso::error::Error;
/// List of the algorithms we support, or Unknown. /// List of the algorithms we support, or Unknown.
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
@ -60,7 +60,7 @@ impl FromStr for Algo {
fn from_str(s: &str) -> Result<Algo, Error> { fn from_str(s: &str) -> Result<Algo, Error> {
Ok(match s { Ok(match s {
"" => return Err(Error::ParseError("'algo' argument cant be empty.")), "" => return Err(Error::Other("'algo' argument cant be empty.")),
"sha-1" => Algo::Sha_1, "sha-1" => Algo::Sha_1,
"sha-256" => Algo::Sha_256, "sha-256" => Algo::Sha_256,
@ -118,7 +118,10 @@ impl Hash {
/// Like [new](#method.new) but takes base64-encoded data before decoding /// Like [new](#method.new) but takes base64-encoded data before decoding
/// it. /// it.
pub fn from_base64(algo: Algo, hash: &str) -> Result<Hash, Error> { pub fn from_base64(algo: Algo, hash: &str) -> Result<Hash, Error> {
Ok(Hash::new(algo, Base64Engine.decode(hash)?)) Ok(Hash::new(
algo,
Base64Engine.decode(hash).map_err(Error::text_parse_error)?,
))
} }
/// Like [new](#method.new) but takes hex-encoded data before decoding it. /// Like [new](#method.new) but takes hex-encoded data before decoding it.
@ -205,6 +208,7 @@ impl Deref for Sha1HexAttribute {
mod tests { mod tests {
use super::*; use super::*;
use crate::Element; use crate::Element;
use xso::error::FromElementError;
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -255,7 +259,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Hash::try_from(elem.clone()).unwrap_err(); let error = Hash::try_from(elem.clone()).unwrap_err();
let returned_elem = match error { let returned_elem = match error {
Error::TypeMismatch(_, _, elem) => elem, FromElementError::Mismatch(elem) => elem,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(elem, returned_elem); assert_eq!(elem, returned_elem);
@ -268,7 +272,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Hash::try_from(elem).unwrap_err(); let error = Hash::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in hash element."); assert_eq!(message, "Unknown child in hash element.");

View file

@ -6,8 +6,8 @@
use crate::iq::{IqGetPayload, IqResultPayload}; use crate::iq::{IqGetPayload, IqResultPayload};
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
generate_element!( generate_element!(
/// Requesting a slot /// Requesting a slot
@ -40,8 +40,8 @@ pub enum Header {
} }
impl TryFrom<Element> for Header { impl TryFrom<Element> for Header {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<Header, Error> { fn try_from(elem: Element) -> Result<Header, FromElementError> {
check_self!(elem, "header", HTTP_UPLOAD); check_self!(elem, "header", HTTP_UPLOAD);
check_no_children!(elem, "header"); check_no_children!(elem, "header");
check_no_unknown_attributes!(elem, "header", ["name"]); check_no_unknown_attributes!(elem, "header", ["name"]);
@ -53,9 +53,10 @@ impl TryFrom<Element> for Header {
"cookie" => Header::Cookie(text), "cookie" => Header::Cookie(text),
"expires" => Header::Expires(text), "expires" => Header::Expires(text),
_ => { _ => {
return Err(Error::ParseError( return Err(Error::Other(
"Header name must be either 'Authorization', 'Cookie', or 'Expires'.", "Header name must be either 'Authorization', 'Cookie', or 'Expires'.",
)) )
.into())
} }
}) })
} }

View file

@ -72,8 +72,8 @@ impl IqSetPayload for Close {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -131,7 +131,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Open::try_from(elem).unwrap_err(); let error = Open::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'block-size' missing."); assert_eq!(message, "Required attribute 'block-size' missing.");
@ -141,7 +141,11 @@ mod tests {
.unwrap(); .unwrap();
let error = Open::try_from(elem).unwrap_err(); let error = Open::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseIntError(error) => error, FromElementError::Invalid(Error::TextParseError(error))
if error.is::<std::num::ParseIntError>() =>
{
error
}
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message.to_string(), "invalid digit found in string"); assert_eq!(message.to_string(), "invalid digit found in string");
@ -151,7 +155,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Open::try_from(elem).unwrap_err(); let error = Open::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(error) => error, FromElementError::Invalid(Error::Other(error)) => error,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'sid' missing."); assert_eq!(message, "Required attribute 'sid' missing.");
@ -162,9 +166,9 @@ mod tests {
let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128' sid='coucou' stanza='fdsq'/>".parse().unwrap(); let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128' sid='coucou' stanza='fdsq'/>".parse().unwrap();
let error = Open::try_from(elem).unwrap_err(); let error = Open::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown value for 'stanza' attribute."); assert_eq!(message.to_string(), "Unknown value for 'stanza' attribute.");
} }
} }

View file

@ -7,9 +7,9 @@
use crate::data_forms::DataForm; use crate::data_forms::DataForm;
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use std::collections::HashMap; use std::collections::HashMap;
use xso::error::{Error, FromElementError};
/// Query for registering against a service. /// Query for registering against a service.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -35,9 +35,9 @@ impl IqSetPayload for Query {}
impl IqResultPayload for Query {} impl IqResultPayload for Query {}
impl TryFrom<Element> for Query { impl TryFrom<Element> for Query {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<Query, Error> { fn try_from(elem: Element) -> Result<Query, FromElementError> {
check_self!(elem, "query", REGISTER, "IBR query"); check_self!(elem, "query", REGISTER, "IBR query");
let mut query = Query { let mut query = Query {
registered: false, registered: false,
@ -76,12 +76,12 @@ impl TryFrom<Element> for Query {
} else if name == "remove" { } else if name == "remove" {
query.remove = true; query.remove = true;
} else { } else {
return Err(Error::ParseError("Wrong field in ibr element.")); return Err(Error::Other("Wrong field in ibr element.").into());
} }
} else if child.is("x", ns::DATA_FORMS) { } else if child.is("x", ns::DATA_FORMS) {
query.form = Some(DataForm::try_from(child.clone())?); query.form = Some(DataForm::try_from(child.clone())?);
} else { } else {
return Err(Error::ParseError("Unknown child in ibr element.")); return Err(Error::Other("Unknown child in ibr element.").into());
} }
} }
Ok(query) Ok(query)

View file

@ -21,9 +21,9 @@ impl PresencePayload for Idle {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use std::str::FromStr; use std::str::FromStr;
use xso::error::{Error, FromElementError};
#[test] #[test]
fn test_size() { fn test_size() {
@ -45,7 +45,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Idle::try_from(elem).unwrap_err(); let error = Idle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in idle element."); assert_eq!(message, "Unknown child in idle element.");
@ -56,7 +56,7 @@ mod tests {
let elem: Element = "<idle xmlns='urn:xmpp:idle:1'/>".parse().unwrap(); let elem: Element = "<idle xmlns='urn:xmpp:idle:1'/>".parse().unwrap();
let error = Idle::try_from(elem).unwrap_err(); let error = Idle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'since' missing."); assert_eq!(message, "Required attribute 'since' missing.");
@ -70,8 +70,12 @@ mod tests {
.unwrap(); .unwrap();
let error = Idle::try_from(elem).unwrap_err(); let error = Idle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ChronoParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string))
_ => panic!(), if string.is::<chrono::ParseError>() =>
{
string
}
other => panic!("unexpected result: {:?}", other),
}; };
assert_eq!(message.to_string(), "input is out of range"); assert_eq!(message.to_string(), "input is out of range");
@ -81,7 +85,11 @@ mod tests {
.unwrap(); .unwrap();
let error = Idle::try_from(elem).unwrap_err(); let error = Idle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ChronoParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string))
if string.is::<chrono::ParseError>() =>
{
string
}
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message.to_string(), "input is out of range"); assert_eq!(message.to_string(), "input is out of range");
@ -92,7 +100,11 @@ mod tests {
.unwrap(); .unwrap();
let error = Idle::try_from(elem).unwrap_err(); let error = Idle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ChronoParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string))
if string.is::<chrono::ParseError>() =>
{
string
}
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message.to_string(), "input contains invalid characters"); assert_eq!(message.to_string(), "input contains invalid characters");
@ -103,7 +115,11 @@ mod tests {
.unwrap(); .unwrap();
let error = Idle::try_from(elem).unwrap_err(); let error = Idle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ChronoParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string))
if string.is::<chrono::ParseError>() =>
{
string
}
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message.to_string(), "input contains invalid characters"); assert_eq!(message.to_string(), "input contains invalid characters");
@ -114,7 +130,11 @@ mod tests {
.unwrap(); .unwrap();
let error = Idle::try_from(elem).unwrap_err(); let error = Idle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ChronoParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string))
if string.is::<chrono::ParseError>() =>
{
string
}
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message.to_string(), "input contains invalid characters"); assert_eq!(message.to_string(), "input contains invalid characters");
@ -125,7 +145,11 @@ mod tests {
.unwrap(); .unwrap();
let error = Idle::try_from(elem).unwrap_err(); let error = Idle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ChronoParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string))
if string.is::<chrono::ParseError>() =>
{
string
}
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message.to_string(), "premature end of input"); assert_eq!(message.to_string(), "premature end of input");

View file

@ -7,10 +7,10 @@
use crate::ns; use crate::ns;
use crate::stanza_error::StanzaError; use crate::stanza_error::StanzaError;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use jid::Jid; use jid::Jid;
use minidom::IntoAttributeValue; use minidom::IntoAttributeValue;
use xso::error::{Error, FromElementError};
/// Should be implemented on every known payload of an `<iq type='get'/>`. /// Should be implemented on every known payload of an `<iq type='get'/>`.
pub trait IqGetPayload: TryFrom<Element> + Into<Element> {} pub trait IqGetPayload: TryFrom<Element> + Into<Element> {}
@ -139,9 +139,9 @@ impl Iq {
} }
impl TryFrom<Element> for Iq { impl TryFrom<Element> for Iq {
type Error = Error; type Error = FromElementError;
fn try_from(root: Element) -> Result<Iq, Error> { fn try_from(root: Element) -> Result<Iq, FromElementError> {
check_self!(root, "iq", DEFAULT_NS); check_self!(root, "iq", DEFAULT_NS);
let from = get_attr!(root, "from", Option); let from = get_attr!(root, "from", Option);
let to = get_attr!(root, "to", Option); let to = get_attr!(root, "to", Option);
@ -152,16 +152,16 @@ impl TryFrom<Element> for Iq {
let mut error_payload = None; let mut error_payload = None;
for elem in root.children() { for elem in root.children() {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError("Wrong number of children in iq element.")); return Err(Error::Other("Wrong number of children in iq element.").into());
} }
if type_ == "error" { if type_ == "error" {
if elem.is("error", ns::DEFAULT_NS) { if elem.is("error", ns::DEFAULT_NS) {
if error_payload.is_some() { if error_payload.is_some() {
return Err(Error::ParseError("Wrong number of children in iq element.")); return Err(Error::Other("Wrong number of children in iq element.").into());
} }
error_payload = Some(StanzaError::try_from(elem.clone())?); error_payload = Some(StanzaError::try_from(elem.clone())?);
} else if root.children().count() != 2 { } else if root.children().count() != 2 {
return Err(Error::ParseError("Wrong number of children in iq element.")); return Err(Error::Other("Wrong number of children in iq element.").into());
} }
} else { } else {
payload = Some(elem.clone()); payload = Some(elem.clone());
@ -172,13 +172,13 @@ impl TryFrom<Element> for Iq {
if let Some(payload) = payload { if let Some(payload) = payload {
IqType::Get(payload) IqType::Get(payload)
} else { } else {
return Err(Error::ParseError("Wrong number of children in iq element.")); return Err(Error::Other("Wrong number of children in iq element.").into());
} }
} else if type_ == "set" { } else if type_ == "set" {
if let Some(payload) = payload { if let Some(payload) = payload {
IqType::Set(payload) IqType::Set(payload)
} else { } else {
return Err(Error::ParseError("Wrong number of children in iq element.")); return Err(Error::Other("Wrong number of children in iq element.").into());
} }
} else if type_ == "result" { } else if type_ == "result" {
if let Some(payload) = payload { if let Some(payload) = payload {
@ -190,10 +190,10 @@ impl TryFrom<Element> for Iq {
if let Some(payload) = error_payload { if let Some(payload) = error_payload {
IqType::Error(payload) IqType::Error(payload)
} else { } else {
return Err(Error::ParseError("Wrong number of children in iq element.")); return Err(Error::Other("Wrong number of children in iq element.").into());
} }
} else { } else {
return Err(Error::ParseError("Unknown iq type.")); return Err(Error::Other("Unknown iq type.").into());
}; };
Ok(Iq { Ok(Iq {
@ -251,7 +251,7 @@ mod tests {
let elem: Element = "<iq xmlns='jabber:component:accept'/>".parse().unwrap(); let elem: Element = "<iq xmlns='jabber:component:accept'/>".parse().unwrap();
let error = Iq::try_from(elem).unwrap_err(); let error = Iq::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'id' missing."); assert_eq!(message, "Required attribute 'id' missing.");
@ -264,7 +264,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Iq::try_from(elem).unwrap_err(); let error = Iq::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'type' missing."); assert_eq!(message, "Required attribute 'type' missing.");
@ -418,7 +418,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Iq::try_from(elem).unwrap_err(); let error = Iq::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Wrong number of children in iq element."); assert_eq!(message, "Wrong number of children in iq element.");

View file

@ -11,12 +11,12 @@ use crate::jingle_ice_udp::Transport as IceUdpTransport;
use crate::jingle_rtp::Description as RtpDescription; use crate::jingle_rtp::Description as RtpDescription;
use crate::jingle_s5b::Transport as Socks5Transport; use crate::jingle_s5b::Transport as Socks5Transport;
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use jid::Jid; use jid::Jid;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use xso::error::{Error, FromElementError};
generate_attribute!( generate_attribute!(
/// The action attribute. /// The action attribute.
@ -428,7 +428,7 @@ impl FromStr for Reason {
"unsupported-applications" => Reason::UnsupportedApplications, "unsupported-applications" => Reason::UnsupportedApplications,
"unsupported-transports" => Reason::UnsupportedTransports, "unsupported-transports" => Reason::UnsupportedTransports,
_ => return Err(Error::ParseError("Unknown reason.")), _ => return Err(Error::Other("Unknown reason.")),
}) })
} }
} }
@ -486,9 +486,9 @@ impl fmt::Display for ReasonElement {
} }
impl TryFrom<Element> for ReasonElement { impl TryFrom<Element> for ReasonElement {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<ReasonElement, Error> { fn try_from(elem: Element) -> Result<ReasonElement, FromElementError> {
check_self!(elem, "reason", JINGLE); check_self!(elem, "reason", JINGLE);
check_no_attributes!(elem, "reason"); check_no_attributes!(elem, "reason");
let mut reason = None; let mut reason = None;
@ -499,24 +499,22 @@ impl TryFrom<Element> for ReasonElement {
check_no_unknown_attributes!(child, "text", ["xml:lang"]); check_no_unknown_attributes!(child, "text", ["xml:lang"]);
let lang = get_attr!(elem, "xml:lang", Default); let lang = get_attr!(elem, "xml:lang", Default);
if texts.insert(lang, child.text()).is_some() { if texts.insert(lang, child.text()).is_some() {
return Err(Error::ParseError( return Err(
"Text element present twice for the same xml:lang.", Error::Other("Text element present twice for the same xml:lang.").into(),
)); );
} }
} else if child.has_ns(ns::JINGLE) { } else if child.has_ns(ns::JINGLE) {
if reason.is_some() { if reason.is_some() {
return Err(Error::ParseError( return Err(Error::Other("Reason must not have more than one reason.").into());
"Reason must not have more than one reason.",
));
} }
check_no_children!(child, "reason"); check_no_children!(child, "reason");
check_no_attributes!(child, "reason"); check_no_attributes!(child, "reason");
reason = Some(child.name().parse()?); reason = Some(child.name().parse()?);
} else { } else {
return Err(Error::ParseError("Reason contains a foreign element.")); return Err(Error::Other("Reason contains a foreign element.").into());
} }
} }
let reason = reason.ok_or(Error::ParseError("Reason doesnt contain a valid reason."))?; let reason = reason.ok_or(Error::Other("Reason doesnt contain a valid reason."))?;
Ok(ReasonElement { reason, texts }) Ok(ReasonElement { reason, texts })
} }
} }
@ -616,9 +614,9 @@ impl Jingle {
} }
impl TryFrom<Element> for Jingle { impl TryFrom<Element> for Jingle {
type Error = Error; type Error = FromElementError;
fn try_from(root: Element) -> Result<Jingle, Error> { fn try_from(root: Element) -> Result<Jingle, FromElementError> {
check_self!(root, "jingle", JINGLE, "Jingle"); check_self!(root, "jingle", JINGLE, "Jingle");
check_no_unknown_attributes!(root, "Jingle", ["action", "initiator", "responder", "sid"]); check_no_unknown_attributes!(root, "Jingle", ["action", "initiator", "responder", "sid"]);
@ -639,17 +637,13 @@ impl TryFrom<Element> for Jingle {
jingle.contents.push(content); jingle.contents.push(content);
} else if child.is("reason", ns::JINGLE) { } else if child.is("reason", ns::JINGLE) {
if jingle.reason.is_some() { if jingle.reason.is_some() {
return Err(Error::ParseError( return Err(Error::Other("Jingle must not have more than one reason.").into());
"Jingle must not have more than one reason.",
));
} }
let reason = ReasonElement::try_from(child)?; let reason = ReasonElement::try_from(child)?;
jingle.reason = Some(reason); jingle.reason = Some(reason);
} else if child.is("group", ns::JINGLE_GROUPING) { } else if child.is("group", ns::JINGLE_GROUPING) {
if jingle.group.is_some() { if jingle.group.is_some() {
return Err(Error::ParseError( return Err(Error::Other("Jingle must not have more than one grouping.").into());
"Jingle must not have more than one grouping.",
));
} }
let group = Group::try_from(child)?; let group = Group::try_from(child)?;
jingle.group = Some(group); jingle.group = Some(group);
@ -726,7 +720,7 @@ mod tests {
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1'/>".parse().unwrap(); let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1'/>".parse().unwrap();
let error = Jingle::try_from(elem).unwrap_err(); let error = Jingle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'action' missing."); assert_eq!(message, "Required attribute 'action' missing.");
@ -736,7 +730,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Jingle::try_from(elem).unwrap_err(); let error = Jingle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'sid' missing."); assert_eq!(message, "Required attribute 'sid' missing.");
@ -746,10 +740,10 @@ mod tests {
.unwrap(); .unwrap();
let error = Jingle::try_from(elem).unwrap_err(); let error = Jingle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown value for 'action' attribute."); assert_eq!(message.to_string(), "Unknown value for 'action' attribute.");
} }
#[test] #[test]
@ -775,7 +769,7 @@ mod tests {
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><content/></jingle>".parse().unwrap(); let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><content/></jingle>".parse().unwrap();
let error = Jingle::try_from(elem).unwrap_err(); let error = Jingle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'creator' missing."); assert_eq!(message, "Required attribute 'creator' missing.");
@ -783,7 +777,7 @@ mod tests {
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><content creator='initiator'/></jingle>".parse().unwrap(); let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><content creator='initiator'/></jingle>".parse().unwrap();
let error = Jingle::try_from(elem).unwrap_err(); let error = Jingle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'name' missing."); assert_eq!(message, "Required attribute 'name' missing.");
@ -791,26 +785,35 @@ mod tests {
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><content creator='coucou' name='coucou'/></jingle>".parse().unwrap(); let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><content creator='coucou' name='coucou'/></jingle>".parse().unwrap();
let error = Jingle::try_from(elem).unwrap_err(); let error = Jingle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string)) => string,
_ => panic!(), other => panic!("unexpected result: {:?}", other),
}; };
assert_eq!(message, "Unknown value for 'creator' attribute."); assert_eq!(
message.to_string(),
"Unknown value for 'creator' attribute."
);
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><content creator='initiator' name='coucou' senders='coucou'/></jingle>".parse().unwrap(); let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><content creator='initiator' name='coucou' senders='coucou'/></jingle>".parse().unwrap();
let error = Jingle::try_from(elem).unwrap_err(); let error = Jingle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown value for 'senders' attribute."); assert_eq!(
message.to_string(),
"Unknown value for 'senders' attribute."
);
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><content creator='initiator' name='coucou' senders=''/></jingle>".parse().unwrap(); let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><content creator='initiator' name='coucou' senders=''/></jingle>".parse().unwrap();
let error = Jingle::try_from(elem).unwrap_err(); let error = Jingle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown value for 'senders' attribute."); assert_eq!(
message.to_string(),
"Unknown value for 'senders' attribute."
);
} }
#[test] #[test]
@ -833,7 +836,7 @@ mod tests {
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><reason/></jingle>".parse().unwrap(); let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><reason/></jingle>".parse().unwrap();
let error = Jingle::try_from(elem).unwrap_err(); let error = Jingle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Reason doesnt contain a valid reason."); assert_eq!(message, "Reason doesnt contain a valid reason.");
@ -841,7 +844,7 @@ mod tests {
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><reason><a/></reason></jingle>".parse().unwrap(); let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><reason><a/></reason></jingle>".parse().unwrap();
let error = Jingle::try_from(elem).unwrap_err(); let error = Jingle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown reason."); assert_eq!(message, "Unknown reason.");
@ -849,7 +852,7 @@ mod tests {
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><reason><a xmlns='http://www.w3.org/1999/xhtml'/></reason></jingle>".parse().unwrap(); let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><reason><a xmlns='http://www.w3.org/1999/xhtml'/></reason></jingle>".parse().unwrap();
let error = Jingle::try_from(elem).unwrap_err(); let error = Jingle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Reason contains a foreign element."); assert_eq!(message, "Reason contains a foreign element.");
@ -857,7 +860,7 @@ mod tests {
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><reason><decline/></reason><reason/></jingle>".parse().unwrap(); let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><reason><decline/></reason><reason/></jingle>".parse().unwrap();
let error = Jingle::try_from(elem).unwrap_err(); let error = Jingle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Jingle must not have more than one reason."); assert_eq!(message, "Jingle must not have more than one reason.");
@ -865,7 +868,7 @@ mod tests {
let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><reason><decline/><text/><text/></reason></jingle>".parse().unwrap(); let elem: Element = "<jingle xmlns='urn:xmpp:jingle:1' action='session-initiate' sid='coucou'><reason><decline/><text/><text/></reason></jingle>".parse().unwrap();
let error = Jingle::try_from(elem).unwrap_err(); let error = Jingle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Text element present twice for the same xml:lang."); assert_eq!(message, "Text element present twice for the same xml:lang.");

View file

@ -5,8 +5,8 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::hashes::{Algo, Hash}; use crate::hashes::{Algo, Hash};
use crate::util::error::Error;
use crate::util::text_node_codecs::{Codec, ColonSeparatedHex}; use crate::util::text_node_codecs::{Codec, ColonSeparatedHex};
use xso::error::Error;
generate_attribute!( generate_attribute!(
/// Indicates which of the end points should initiate the TCP connection establishment. /// Indicates which of the end points should initiate the TCP connection establishment.
@ -64,7 +64,7 @@ impl Fingerprint {
hash: &str, hash: &str,
) -> Result<Fingerprint, Error> { ) -> Result<Fingerprint, Error> {
let algo = algo.parse()?; let algo = algo.parse()?;
let hash = Hash::from_colon_separated_hex(algo, hash)?; let hash = Hash::from_colon_separated_hex(algo, hash).map_err(Error::text_parse_error)?;
Ok(Fingerprint::from_hash(setup, hash)) Ok(Fingerprint::from_hash(setup, hash))
} }
} }

View file

@ -8,10 +8,10 @@ use crate::date::DateTime;
use crate::hashes::Hash; use crate::hashes::Hash;
use crate::jingle::{ContentId, Creator}; use crate::jingle::{ContentId, Creator};
use crate::ns; use crate::ns;
use crate::util::error::Error;
use minidom::{Element, Node}; use minidom::{Element, Node};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::str::FromStr; use std::str::FromStr;
use xso::error::{Error, FromElementError};
generate_element!( generate_element!(
/// Represents a range in a file. /// Represents a range in a file.
@ -85,7 +85,7 @@ impl File {
/// Sets the date of last modification on this file from an ISO-8601 /// Sets the date of last modification on this file from an ISO-8601
/// string. /// string.
pub fn with_date_str(mut self, date: &str) -> Result<File, Error> { pub fn with_date_str(mut self, date: &str) -> Result<File, Error> {
self.date = Some(DateTime::from_str(date)?); self.date = Some(DateTime::from_str(date).map_err(Error::text_parse_error)?);
Ok(self) Ok(self)
} }
@ -127,9 +127,9 @@ impl File {
} }
impl TryFrom<Element> for File { impl TryFrom<Element> for File {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<File, Error> { fn try_from(elem: Element) -> Result<File, FromElementError> {
check_self!(elem, "file", JINGLE_FT); check_self!(elem, "file", JINGLE_FT);
check_no_attributes!(elem, "file"); check_no_attributes!(elem, "file");
@ -146,43 +146,41 @@ impl TryFrom<Element> for File {
for child in elem.children() { for child in elem.children() {
if child.is("date", ns::JINGLE_FT) { if child.is("date", ns::JINGLE_FT) {
if file.date.is_some() { if file.date.is_some() {
return Err(Error::ParseError("File must not have more than one date.")); return Err(Error::Other("File must not have more than one date.").into());
} }
file.date = Some(child.text().parse()?); file.date = Some(child.text().parse().map_err(Error::text_parse_error)?);
} else if child.is("media-type", ns::JINGLE_FT) { } else if child.is("media-type", ns::JINGLE_FT) {
if file.media_type.is_some() { if file.media_type.is_some() {
return Err(Error::ParseError( return Err(Error::Other("File must not have more than one media-type.").into());
"File must not have more than one media-type.",
));
} }
file.media_type = Some(child.text()); file.media_type = Some(child.text());
} else if child.is("name", ns::JINGLE_FT) { } else if child.is("name", ns::JINGLE_FT) {
if file.name.is_some() { if file.name.is_some() {
return Err(Error::ParseError("File must not have more than one name.")); return Err(Error::Other("File must not have more than one name.").into());
} }
file.name = Some(child.text()); file.name = Some(child.text());
} else if child.is("desc", ns::JINGLE_FT) { } else if child.is("desc", ns::JINGLE_FT) {
let lang = get_attr!(child, "xml:lang", Default); let lang = get_attr!(child, "xml:lang", Default);
let desc = Desc(child.text()); let desc = Desc(child.text());
if file.descs.insert(lang, desc).is_some() { if file.descs.insert(lang, desc).is_some() {
return Err(Error::ParseError( return Err(
"Desc element present twice for the same xml:lang.", Error::Other("Desc element present twice for the same xml:lang.").into(),
)); );
} }
} else if child.is("size", ns::JINGLE_FT) { } else if child.is("size", ns::JINGLE_FT) {
if file.size.is_some() { if file.size.is_some() {
return Err(Error::ParseError("File must not have more than one size.")); return Err(Error::Other("File must not have more than one size.").into());
} }
file.size = Some(child.text().parse()?); file.size = Some(child.text().parse().map_err(Error::text_parse_error)?);
} else if child.is("range", ns::JINGLE_FT) { } else if child.is("range", ns::JINGLE_FT) {
if file.range.is_some() { if file.range.is_some() {
return Err(Error::ParseError("File must not have more than one range.")); return Err(Error::Other("File must not have more than one range.").into());
} }
file.range = Some(Range::try_from(child.clone())?); file.range = Some(Range::try_from(child.clone())?);
} else if child.is("hash", ns::HASHES) { } else if child.is("hash", ns::HASHES) {
file.hashes.push(Hash::try_from(child.clone())?); file.hashes.push(Hash::try_from(child.clone())?);
} else { } else {
return Err(Error::ParseError("Unknown element in JingleFT file.")); return Err(Error::Other("Unknown element in JingleFT file.").into());
} }
} }
@ -230,24 +228,25 @@ pub struct Description {
} }
impl TryFrom<Element> for Description { impl TryFrom<Element> for Description {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<Description, Error> { fn try_from(elem: Element) -> Result<Description, FromElementError> {
check_self!(elem, "description", JINGLE_FT, "JingleFT description"); check_self!(elem, "description", JINGLE_FT, "JingleFT description");
check_no_attributes!(elem, "JingleFT description"); check_no_attributes!(elem, "JingleFT description");
let mut file = None; let mut file = None;
for child in elem.children() { for child in elem.children() {
if file.is_some() { if file.is_some() {
return Err(Error::ParseError( return Err(Error::Other(
"JingleFT description element must have exactly one child.", "JingleFT description element must have exactly one child.",
)); )
.into());
} }
file = Some(File::try_from(child.clone())?); file = Some(File::try_from(child.clone())?);
} }
if file.is_none() { if file.is_none() {
return Err(Error::ParseError( return Err(
"JingleFT description element must have exactly one child.", Error::Other("JingleFT description element must have exactly one child.").into(),
)); );
} }
Ok(Description { Ok(Description {
file: file.unwrap(), file: file.unwrap(),
@ -277,24 +276,30 @@ pub struct Checksum {
} }
impl TryFrom<Element> for Checksum { impl TryFrom<Element> for Checksum {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<Checksum, Error> { fn try_from(elem: Element) -> Result<Checksum, FromElementError> {
check_self!(elem, "checksum", JINGLE_FT); check_self!(elem, "checksum", JINGLE_FT);
check_no_unknown_attributes!(elem, "checksum", ["name", "creator"]); check_no_unknown_attributes!(elem, "checksum", ["name", "creator"]);
let mut file = None; let mut file = None;
for child in elem.children() { for child in elem.children() {
if file.is_some() { if file.is_some() {
return Err(Error::ParseError( return Err(
"JingleFT checksum element must have exactly one child.", Error::Other("JingleFT checksum element must have exactly one child.").into(),
)); );
} }
file = Some(File::try_from(child.clone()).map_err(|e| e.hide_type_mismatch())?); file = Some(match File::try_from(child.clone()) {
Ok(v) => v,
Err(FromElementError::Mismatch(_)) => {
return Err(Error::Other("Unexpected child element").into())
}
Err(other) => return Err(other),
});
} }
if file.is_none() { if file.is_none() {
return Err(Error::ParseError( return Err(
"JingleFT checksum element must have exactly one child.", Error::Other("JingleFT checksum element must have exactly one child.").into(),
)); );
} }
Ok(Checksum { Ok(Checksum {
name: get_attr!(elem, "name", Required), name: get_attr!(elem, "name", Required),
@ -451,7 +456,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Description::try_from(elem).unwrap_err(); let error = Description::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Desc element present twice for the same xml:lang."); assert_eq!(message, "Desc element present twice for the same xml:lang.");
@ -471,7 +476,7 @@ mod tests {
let elem: Element = "<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator'><coucou/></received>".parse().unwrap(); let elem: Element = "<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator'><coucou/></received>".parse().unwrap();
let error = Received::try_from(elem).unwrap_err(); let error = Received::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in received element."); assert_eq!(message, "Unknown child in received element.");
@ -482,7 +487,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Received::try_from(elem).unwrap_err(); let error = Received::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'name' missing."); assert_eq!(message, "Required attribute 'name' missing.");
@ -490,10 +495,13 @@ mod tests {
let elem: Element = "<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='coucou'/>".parse().unwrap(); let elem: Element = "<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='coucou'/>".parse().unwrap();
let error = Received::try_from(elem).unwrap_err(); let error = Received::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown value for 'creator' attribute."); assert_eq!(
message.to_string(),
"Unknown value for 'creator' attribute."
);
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(not(feature = "disable-validation"))]
@ -502,7 +510,7 @@ mod tests {
let elem: Element = "<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator' coucou=''/>".parse().unwrap(); let elem: Element = "<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator' coucou=''/>".parse().unwrap();
let error = Received::try_from(elem).unwrap_err(); let error = Received::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in received element."); assert_eq!(message, "Unknown attribute in received element.");
@ -540,7 +548,7 @@ mod tests {
let elem: Element = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator'><coucou/></checksum>".parse().unwrap(); 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(); let error = Checksum::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
other => panic!("unexpected error: {:?}", other), other => panic!("unexpected error: {:?}", other),
}; };
assert_eq!(message, "Unexpected child element"); assert_eq!(message, "Unexpected child element");
@ -548,7 +556,7 @@ mod tests {
let elem: Element = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' creator='initiator'><file><hash xmlns='urn:xmpp:hashes:2' algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash></file></checksum>".parse().unwrap(); let elem: Element = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' creator='initiator'><file><hash xmlns='urn:xmpp:hashes:2' algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash></file></checksum>".parse().unwrap();
let error = Checksum::try_from(elem).unwrap_err(); let error = Checksum::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'name' missing."); assert_eq!(message, "Required attribute 'name' missing.");
@ -556,10 +564,13 @@ mod tests {
let elem: Element = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='coucou'><file><hash xmlns='urn:xmpp:hashes:2' algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash></file></checksum>".parse().unwrap(); let elem: Element = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='coucou'><file><hash xmlns='urn:xmpp:hashes:2' algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash></file></checksum>".parse().unwrap();
let error = Checksum::try_from(elem).unwrap_err(); let error = Checksum::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown value for 'creator' attribute."); assert_eq!(
message.to_string(),
"Unknown value for 'creator' attribute."
);
} }
#[cfg(not(feature = "disable-validation"))] #[cfg(not(feature = "disable-validation"))]
@ -568,7 +579,7 @@ mod tests {
let elem: Element = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator' coucou=''><file><hash xmlns='urn:xmpp:hashes:2' algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash></file></checksum>".parse().unwrap(); let elem: Element = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='initiator' coucou=''><file><hash xmlns='urn:xmpp:hashes:2' algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash></file></checksum>".parse().unwrap();
let error = Checksum::try_from(elem).unwrap_err(); let error = Checksum::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in checksum element."); assert_eq!(message, "Unknown attribute in checksum element.");
@ -611,7 +622,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Range::try_from(elem).unwrap_err(); let error = Range::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in range element."); assert_eq!(message, "Unknown attribute in range element.");

View file

@ -24,8 +24,8 @@ attributes: [
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -58,7 +58,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Transport::try_from(elem).unwrap_err(); let error = Transport::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'block-size' missing."); assert_eq!(message, "Required attribute 'block-size' missing.");
@ -69,7 +69,11 @@ mod tests {
.unwrap(); .unwrap();
let error = Transport::try_from(elem).unwrap_err(); let error = Transport::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseIntError(error) => error, FromElementError::Invalid(Error::TextParseError(error))
if error.is::<std::num::ParseIntError>() =>
{
error
}
_ => panic!(), _ => panic!(),
}; };
assert_eq!( assert_eq!(
@ -82,7 +86,11 @@ mod tests {
.unwrap(); .unwrap();
let error = Transport::try_from(elem).unwrap_err(); let error = Transport::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseIntError(error) => error, FromElementError::Invalid(Error::TextParseError(error))
if error.is::<std::num::ParseIntError>() =>
{
error
}
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message.to_string(), "invalid digit found in string"); assert_eq!(message.to_string(), "invalid digit found in string");
@ -93,7 +101,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Transport::try_from(elem).unwrap_err(); let error = Transport::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'sid' missing."); assert_eq!(message, "Required attribute 'sid' missing.");
@ -104,9 +112,9 @@ mod tests {
let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='128' sid='coucou' stanza='fdsq'/>".parse().unwrap(); let elem: Element = "<transport xmlns='urn:xmpp:jingle:transports:ibb:1' block-size='128' sid='coucou' stanza='fdsq'/>".parse().unwrap();
let error = Transport::try_from(elem).unwrap_err(); let error = Transport::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown value for 'stanza' attribute."); assert_eq!(message.to_string(), "Unknown value for 'stanza' attribute.");
} }
} }

View file

@ -6,8 +6,8 @@
use crate::jingle::SessionId; use crate::jingle::SessionId;
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
/// Defines a protocol for broadcasting Jingle requests to all of the clients /// Defines a protocol for broadcasting Jingle requests to all of the clients
/// of a user. /// of a user.
@ -47,27 +47,27 @@ fn check_empty_and_get_sid(elem: Element) -> Result<SessionId, Error> {
} }
impl TryFrom<Element> for JingleMI { impl TryFrom<Element> for JingleMI {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<JingleMI, Error> { fn try_from(elem: Element) -> Result<JingleMI, FromElementError> {
if !elem.has_ns(ns::JINGLE_MESSAGE) { if !elem.has_ns(ns::JINGLE_MESSAGE) {
return Err(Error::ParseError("This is not a Jingle message element.")); return Err(Error::Other("This is not a Jingle message element.").into());
} }
Ok(match elem.name() { Ok(match elem.name() {
"propose" => { "propose" => {
let mut description = None; let mut description = None;
for child in elem.children() { for child in elem.children() {
if child.name() != "description" { if child.name() != "description" {
return Err(Error::ParseError("Unknown child in propose element.")); return Err(Error::Other("Unknown child in propose element.").into());
} }
if description.is_some() { if description.is_some() {
return Err(Error::ParseError("Too many children in propose element.")); return Err(Error::Other("Too many children in propose element.").into());
} }
description = Some(child.clone()); description = Some(child.clone());
} }
JingleMI::Propose { JingleMI::Propose {
sid: get_sid(elem)?, sid: get_sid(elem)?,
description: description.ok_or(Error::ParseError( description: description.ok_or(Error::Other(
"Propose element doesnt contain a description.", "Propose element doesnt contain a description.",
))?, ))?,
} }
@ -76,7 +76,7 @@ impl TryFrom<Element> for JingleMI {
"accept" => JingleMI::Accept(check_empty_and_get_sid(elem)?), "accept" => JingleMI::Accept(check_empty_and_get_sid(elem)?),
"proceed" => JingleMI::Proceed(check_empty_and_get_sid(elem)?), "proceed" => JingleMI::Proceed(check_empty_and_get_sid(elem)?),
"reject" => JingleMI::Reject(check_empty_and_get_sid(elem)?), "reject" => JingleMI::Reject(check_empty_and_get_sid(elem)?),
_ => return Err(Error::ParseError("This is not a Jingle message element.")), _ => return Err(Error::Other("This is not a Jingle message element.").into()),
}) })
} }
} }
@ -134,7 +134,7 @@ mod tests {
.unwrap(); .unwrap();
let error = JingleMI::try_from(elem).unwrap_err(); let error = JingleMI::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in propose element."); assert_eq!(message, "Unknown child in propose element.");

View file

@ -5,10 +5,10 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use jid::Jid; use jid::Jid;
use std::net::IpAddr; use std::net::IpAddr;
use xso::error::{Error, FromElementError};
generate_attribute!( generate_attribute!(
/// The type of the connection being proposed by this candidate. /// The type of the connection being proposed by this candidate.
@ -171,9 +171,9 @@ impl Transport {
} }
impl TryFrom<Element> for Transport { impl TryFrom<Element> for Transport {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<Transport, Error> { fn try_from(elem: Element) -> Result<Transport, FromElementError> {
check_self!(elem, "transport", JINGLE_S5B); check_self!(elem, "transport", JINGLE_S5B);
check_no_unknown_attributes!(elem, "transport", ["sid", "dstaddr", "mode"]); check_no_unknown_attributes!(elem, "transport", ["sid", "dstaddr", "mode"]);
let sid = get_attr!(elem, "sid", Required); let sid = get_attr!(elem, "sid", Required);
@ -186,47 +186,50 @@ impl TryFrom<Element> for Transport {
let mut candidates = let mut candidates =
match payload { match payload {
Some(TransportPayload::Candidates(candidates)) => candidates, Some(TransportPayload::Candidates(candidates)) => candidates,
Some(_) => return Err(Error::ParseError( Some(_) => return Err(Error::Other(
"Non-candidate child already present in JingleS5B transport element.", "Non-candidate child already present in JingleS5B transport element.",
)), )
.into()),
None => vec![], None => vec![],
}; };
candidates.push(Candidate::try_from(child.clone())?); candidates.push(Candidate::try_from(child.clone())?);
TransportPayload::Candidates(candidates) TransportPayload::Candidates(candidates)
} else if child.is("activated", ns::JINGLE_S5B) { } else if child.is("activated", ns::JINGLE_S5B) {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError( return Err(Error::Other(
"Non-activated child already present in JingleS5B transport element.", "Non-activated child already present in JingleS5B transport element.",
)); )
.into());
} }
let cid = get_attr!(child, "cid", Required); let cid = get_attr!(child, "cid", Required);
TransportPayload::Activated(cid) TransportPayload::Activated(cid)
} else if child.is("candidate-error", ns::JINGLE_S5B) { } else if child.is("candidate-error", ns::JINGLE_S5B) {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError( return Err(Error::Other(
"Non-candidate-error child already present in JingleS5B transport element.", "Non-candidate-error child already present in JingleS5B transport element.",
)); )
.into());
} }
TransportPayload::CandidateError TransportPayload::CandidateError
} else if child.is("candidate-used", ns::JINGLE_S5B) { } else if child.is("candidate-used", ns::JINGLE_S5B) {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError( return Err(Error::Other(
"Non-candidate-used child already present in JingleS5B transport element.", "Non-candidate-used child already present in JingleS5B transport element.",
)); )
.into());
} }
let cid = get_attr!(child, "cid", Required); let cid = get_attr!(child, "cid", Required);
TransportPayload::CandidateUsed(cid) TransportPayload::CandidateUsed(cid)
} else if child.is("proxy-error", ns::JINGLE_S5B) { } else if child.is("proxy-error", ns::JINGLE_S5B) {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError( return Err(Error::Other(
"Non-proxy-error child already present in JingleS5B transport element.", "Non-proxy-error child already present in JingleS5B transport element.",
)); )
.into());
} }
TransportPayload::ProxyError TransportPayload::ProxyError
} else { } else {
return Err(Error::ParseError( return Err(Error::Other("Unknown child in JingleS5B transport element.").into());
"Unknown child in JingleS5B transport element.",
));
}); });
} }
let payload = payload.unwrap_or(TransportPayload::None); let payload = payload.unwrap_or(TransportPayload::None);

View file

@ -23,7 +23,7 @@
#![warn(missing_docs)] #![warn(missing_docs)]
pub use crate::util::error::Error; pub use xso::error::{Error, FromElementError};
// TODO: only export top-level module on the next major release // TODO: only export top-level module on the next major release
pub use jid::{self, BareJid, Error as JidParseError, FullJid, Jid}; pub use jid::{self, BareJid, Error as JidParseError, FullJid, Jid};
pub use minidom::Element; pub use minidom::Element;

View file

@ -11,9 +11,9 @@ use crate::message::MessagePayload;
use crate::ns; use crate::ns;
use crate::pubsub::NodeName; use crate::pubsub::NodeName;
use crate::rsm::{SetQuery, SetResult}; use crate::rsm::{SetQuery, SetResult};
use crate::util::error::Error;
use crate::Element; use crate::Element;
use minidom::Node; use minidom::Node;
use xso::error::{Error, FromElementError};
generate_id!( generate_id!(
/// An identifier matching a result message to the query requesting it. /// An identifier matching a result message to the query requesting it.
@ -41,8 +41,8 @@ impl IqSetPayload for Query {}
impl IqResultPayload for Query {} impl IqResultPayload for Query {}
impl TryFrom<Element> for Query { impl TryFrom<Element> for Query {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<Query, Error> { fn try_from(elem: Element) -> Result<Query, FromElementError> {
check_self!(elem, "query", MAM); check_self!(elem, "query", MAM);
check_no_unknown_attributes!(elem, "query", ["queryid", "node"]); check_no_unknown_attributes!(elem, "query", ["queryid", "node"]);
@ -52,32 +52,34 @@ impl TryFrom<Element> for Query {
for child in elem.children() { for child in elem.children() {
if child.is("x", ns::DATA_FORMS) { if child.is("x", ns::DATA_FORMS) {
if form.is_some() { if form.is_some() {
return Err(Error::ParseError( return Err(
"Element query must not have more than one x child.", Error::Other("Element query must not have more than one x child.").into(),
)); );
} }
form = Some(DataForm::try_from(child.clone())?); form = Some(DataForm::try_from(child.clone())?);
continue; continue;
} }
if child.is("set", ns::RSM) { if child.is("set", ns::RSM) {
if set.is_some() { if set.is_some() {
return Err(Error::ParseError( return Err(Error::Other(
"Element query must not have more than one set child.", "Element query must not have more than one set child.",
)); )
.into());
} }
set = Some(SetQuery::try_from(child.clone())?); set = Some(SetQuery::try_from(child.clone())?);
continue; continue;
} }
if child.is("flip-page", ns::MAM) { if child.is("flip-page", ns::MAM) {
if flip_page.is_some() { if flip_page.is_some() {
return Err(Error::ParseError( return Err(Error::Other(
"Element query must not have more than one flip-page child.", "Element query must not have more than one flip-page child.",
)); )
.into());
} }
flip_page = Some(true); flip_page = Some(true);
continue; continue;
} }
return Err(Error::ParseError("Unknown child in query element.")); return Err(Error::Other("Unknown child in query element.").into());
} }
Ok(Query { Ok(Query {
queryid: match elem.attr("queryid") { queryid: match elem.attr("queryid") {
@ -296,7 +298,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Query::try_from(elem).unwrap_err(); let error = Query::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in query element."); assert_eq!(message, "Unknown child in query element.");

View file

@ -6,9 +6,9 @@
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
use crate::ns; use crate::ns;
use crate::util::error::Error;
use jid::Jid; use jid::Jid;
use minidom::{Element, Node}; use minidom::{Element, Node};
use xso::error::{Error, FromElementError};
generate_attribute!( generate_attribute!(
/// Notes the default archiving preference for the user. /// Notes the default archiving preference for the user.
@ -44,9 +44,9 @@ impl IqSetPayload for Prefs {}
impl IqResultPayload for Prefs {} impl IqResultPayload for Prefs {}
impl TryFrom<Element> for Prefs { impl TryFrom<Element> for Prefs {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<Prefs, Error> { fn try_from(elem: Element) -> Result<Prefs, FromElementError> {
check_self!(elem, "prefs", MAM); check_self!(elem, "prefs", MAM);
check_no_unknown_attributes!(elem, "prefs", ["default"]); check_no_unknown_attributes!(elem, "prefs", ["default"]);
let mut always = vec![]; let mut always = vec![];
@ -55,19 +55,19 @@ impl TryFrom<Element> for Prefs {
if child.is("always", ns::MAM) { if child.is("always", ns::MAM) {
for jid_elem in child.children() { for jid_elem in child.children() {
if !jid_elem.is("jid", ns::MAM) { if !jid_elem.is("jid", ns::MAM) {
return Err(Error::ParseError("Invalid jid element in always.")); return Err(Error::Other("Invalid jid element in always.").into());
} }
always.push(jid_elem.text().parse()?); always.push(jid_elem.text().parse().map_err(Error::text_parse_error)?);
} }
} else if child.is("never", ns::MAM) { } else if child.is("never", ns::MAM) {
for jid_elem in child.children() { for jid_elem in child.children() {
if !jid_elem.is("jid", ns::MAM) { if !jid_elem.is("jid", ns::MAM) {
return Err(Error::ParseError("Invalid jid element in never.")); return Err(Error::Other("Invalid jid element in never.").into());
} }
never.push(jid_elem.text().parse()?); never.push(jid_elem.text().parse().map_err(Error::text_parse_error)?);
} }
} else { } else {
return Err(Error::ParseError("Unknown child in prefs element.")); return Err(Error::Other("Unknown child in prefs element.").into());
} }
} }
let default_ = get_attr!(elem, "default", Required); let default_ = get_attr!(elem, "default", Required);

View file

@ -46,8 +46,8 @@ generate_element!(
mod tests { mod tests {
use super::*; use super::*;
use crate::data_forms::DataForm; use crate::data_forms::DataForm;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -98,7 +98,11 @@ mod tests {
.unwrap(); .unwrap();
let error = MediaElement::try_from(elem).unwrap_err(); let error = MediaElement::try_from(elem).unwrap_err();
let error = match error { let error = match error {
Error::ParseIntError(error) => error, FromElementError::Invalid(Error::TextParseError(error))
if error.is::<std::num::ParseIntError>() =>
{
error
}
_ => panic!(), _ => panic!(),
}; };
assert_eq!(error.to_string(), "cannot parse integer from empty string"); assert_eq!(error.to_string(), "cannot parse integer from empty string");
@ -108,7 +112,11 @@ mod tests {
.unwrap(); .unwrap();
let error = MediaElement::try_from(elem).unwrap_err(); let error = MediaElement::try_from(elem).unwrap_err();
let error = match error { let error = match error {
Error::ParseIntError(error) => error, FromElementError::Invalid(Error::TextParseError(error))
if error.is::<std::num::ParseIntError>() =>
{
error
}
_ => panic!(), _ => panic!(),
}; };
assert_eq!(error.to_string(), "invalid digit found in string"); assert_eq!(error.to_string(), "invalid digit found in string");
@ -118,7 +126,11 @@ mod tests {
.unwrap(); .unwrap();
let error = MediaElement::try_from(elem).unwrap_err(); let error = MediaElement::try_from(elem).unwrap_err();
let error = match error { let error = match error {
Error::ParseIntError(error) => error, FromElementError::Invalid(Error::TextParseError(error))
if error.is::<std::num::ParseIntError>() =>
{
error
}
_ => panic!(), _ => panic!(),
}; };
assert_eq!(error.to_string(), "cannot parse integer from empty string"); assert_eq!(error.to_string(), "cannot parse integer from empty string");
@ -128,7 +140,11 @@ mod tests {
.unwrap(); .unwrap();
let error = MediaElement::try_from(elem).unwrap_err(); let error = MediaElement::try_from(elem).unwrap_err();
let error = match error { let error = match error {
Error::ParseIntError(error) => error, FromElementError::Invalid(Error::TextParseError(error))
if error.is::<std::num::ParseIntError>() =>
{
error
}
_ => panic!(), _ => panic!(),
}; };
assert_eq!(error.to_string(), "invalid digit found in string"); assert_eq!(error.to_string(), "invalid digit found in string");
@ -141,7 +157,7 @@ mod tests {
.unwrap(); .unwrap();
let error = MediaElement::try_from(elem).unwrap_err(); let error = MediaElement::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in media element."); assert_eq!(message, "Unknown child in media element.");
@ -155,7 +171,7 @@ mod tests {
.unwrap(); .unwrap();
let error = MediaElement::try_from(elem).unwrap_err(); let error = MediaElement::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'type' missing."); assert_eq!(message, "Required attribute 'type' missing.");
@ -165,7 +181,7 @@ mod tests {
.unwrap(); .unwrap();
let error = MediaElement::try_from(elem).unwrap_err(); let error = MediaElement::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!( assert_eq!(

View file

@ -5,10 +5,10 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use jid::Jid; use jid::Jid;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use xso::error::{Error, FromElementError};
/// Should be implemented on every known payload of a `<message/>`. /// Should be implemented on every known payload of a `<message/>`.
pub trait MessagePayload: TryFrom<Element> + Into<Element> {} pub trait MessagePayload: TryFrom<Element> + Into<Element> {}
@ -213,7 +213,7 @@ impl Message {
/// the message. /// the message.
/// ///
/// Elements which do not match the given type are not removed. /// Elements which do not match the given type are not removed.
pub fn extract_payload<T: TryFrom<Element, Error = Error>>( pub fn extract_payload<T: TryFrom<Element, Error = FromElementError>>(
&mut self, &mut self,
) -> Result<Option<T>, Error> { ) -> Result<Option<T>, Error> {
let mut buf = Vec::with_capacity(self.payloads.len()); let mut buf = Vec::with_capacity(self.payloads.len());
@ -225,10 +225,10 @@ impl Message {
result = Ok(Some(v)); result = Ok(Some(v));
break; break;
} }
Err(Error::TypeMismatch(_, _, residual)) => { Err(FromElementError::Mismatch(residual)) => {
buf.push(residual); buf.push(residual);
} }
Err(other) => { Err(FromElementError::Invalid(other)) => {
result = Err(other); result = Err(other);
break; break;
} }
@ -241,9 +241,9 @@ impl Message {
} }
impl TryFrom<Element> for Message { impl TryFrom<Element> for Message {
type Error = Error; type Error = FromElementError;
fn try_from(root: Element) -> Result<Message, Error> { fn try_from(root: Element) -> Result<Message, FromElementError> {
check_self!(root, "message", DEFAULT_NS); check_self!(root, "message", DEFAULT_NS);
let from = get_attr!(root, "from", Option); let from = get_attr!(root, "from", Option);
let to = get_attr!(root, "to", Option); let to = get_attr!(root, "to", Option);
@ -259,22 +259,23 @@ impl TryFrom<Element> for Message {
let lang = get_attr!(elem, "xml:lang", Default); let lang = get_attr!(elem, "xml:lang", Default);
let body = Body(elem.text()); let body = Body(elem.text());
if bodies.insert(lang, body).is_some() { if bodies.insert(lang, body).is_some() {
return Err(Error::ParseError( return Err(
"Body element present twice for the same xml:lang.", Error::Other("Body element present twice for the same xml:lang.").into(),
)); );
} }
} else if elem.is("subject", ns::DEFAULT_NS) { } else if elem.is("subject", ns::DEFAULT_NS) {
check_no_children!(elem, "subject"); check_no_children!(elem, "subject");
let lang = get_attr!(elem, "xml:lang", Default); let lang = get_attr!(elem, "xml:lang", Default);
let subject = Subject(elem.text()); let subject = Subject(elem.text());
if subjects.insert(lang, subject).is_some() { if subjects.insert(lang, subject).is_some() {
return Err(Error::ParseError( return Err(Error::Other(
"Subject element present twice for the same xml:lang.", "Subject element present twice for the same xml:lang.",
)); )
.into());
} }
} else if elem.is("thread", ns::DEFAULT_NS) { } else if elem.is("thread", ns::DEFAULT_NS) {
if thread.is_some() { if thread.is_some() {
return Err(Error::ParseError("Thread element present twice.")); return Err(Error::Other("Thread element present twice.").into());
} }
check_no_children!(elem, "thread"); check_no_children!(elem, "thread");
thread = Some(Thread(elem.text())); thread = Some(Thread(elem.text()));

View file

@ -21,8 +21,8 @@ impl MessagePayload for Replace {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -52,7 +52,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Replace::try_from(elem).unwrap_err(); let error = Replace::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in replace element."); assert_eq!(message, "Unknown attribute in replace element.");
@ -65,7 +65,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Replace::try_from(elem).unwrap_err(); let error = Replace::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in replace element."); assert_eq!(message, "Unknown child in replace element.");
@ -78,7 +78,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Replace::try_from(elem).unwrap_err(); let error = Replace::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'id' missing."); assert_eq!(message, "Required attribute 'id' missing.");

View file

@ -94,9 +94,9 @@ impl Muc {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use std::str::FromStr; use std::str::FromStr;
use xso::error::{Error, FromElementError};
#[test] #[test]
fn test_muc_simple() { fn test_muc_simple() {
@ -113,7 +113,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Muc::try_from(elem).unwrap_err(); let error = Muc::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in x element."); assert_eq!(message, "Unknown child in x element.");
@ -140,7 +140,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Muc::try_from(elem).unwrap_err(); let error = Muc::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in x element."); assert_eq!(message, "Unknown attribute in x element.");

View file

@ -8,8 +8,8 @@
use crate::message::MessagePayload; use crate::message::MessagePayload;
use crate::ns; use crate::ns;
use crate::presence::PresencePayload; use crate::presence::PresencePayload;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
use jid::FullJid; use jid::FullJid;
@ -94,9 +94,9 @@ pub enum Actor {
} }
impl TryFrom<Element> for Actor { impl TryFrom<Element> for Actor {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<Actor, Error> { fn try_from(elem: Element) -> Result<Actor, FromElementError> {
check_self!(elem, "actor", MUC_USER); check_self!(elem, "actor", MUC_USER);
check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]); check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]);
check_no_children!(elem, "actor"); check_no_children!(elem, "actor");
@ -104,9 +104,9 @@ impl TryFrom<Element> for Actor {
let nick = get_attr!(elem, "nick", Option); let nick = get_attr!(elem, "nick", Option);
match (jid, nick) { match (jid, nick) {
(Some(_), Some(_)) | (None, None) => Err(Error::ParseError( (Some(_), Some(_)) | (None, None) => {
"Either 'jid' or 'nick' attribute is required.", Err(Error::Other("Either 'jid' or 'nick' attribute is required.").into())
)), }
(Some(jid), _) => Ok(Actor::Jid(jid)), (Some(jid), _) => Ok(Actor::Jid(jid)),
(_, Some(nick)) => Ok(Actor::Nick(nick)), (_, Some(nick)) => Ok(Actor::Nick(nick)),
} }
@ -343,7 +343,7 @@ mod tests {
.unwrap(); .unwrap();
let error = MucUser::try_from(elem).unwrap_err(); let error = MucUser::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in x element."); assert_eq!(message, "Unknown child in x element.");
@ -370,7 +370,7 @@ mod tests {
.unwrap(); .unwrap();
let error = MucUser::try_from(elem).unwrap_err(); let error = MucUser::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in x element."); assert_eq!(message, "Unknown attribute in x element.");
@ -391,7 +391,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Status::try_from(elem).unwrap_err(); let error = Status::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'code' missing."); assert_eq!(message, "Required attribute 'code' missing.");
@ -407,7 +407,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Status::try_from(elem).unwrap_err(); let error = Status::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in status element."); assert_eq!(message, "Unknown child in status element.");
@ -429,7 +429,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Status::try_from(elem).unwrap_err(); let error = Status::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Invalid status code value."); assert_eq!(message, "Invalid status code value.");
@ -442,7 +442,11 @@ mod tests {
.unwrap(); .unwrap();
let error = Status::try_from(elem).unwrap_err(); let error = Status::try_from(elem).unwrap_err();
let error = match error { let error = match error {
Error::ParseIntError(error) => error, FromElementError::Invalid(Error::TextParseError(error))
if error.is::<std::num::ParseIntError>() =>
{
error
}
_ => panic!(), _ => panic!(),
}; };
assert_eq!(error.to_string(), "invalid digit found in string"); assert_eq!(error.to_string(), "invalid digit found in string");
@ -455,7 +459,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Actor::try_from(elem).unwrap_err(); let error = Actor::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Either 'jid' or 'nick' attribute is required."); assert_eq!(message, "Either 'jid' or 'nick' attribute is required.");
@ -470,7 +474,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Actor::try_from(elem).unwrap_err(); let error = Actor::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Either 'jid' or 'nick' attribute is required."); assert_eq!(message, "Either 'jid' or 'nick' attribute is required.");
@ -530,7 +534,7 @@ mod tests {
.unwrap(); .unwrap();
let continue_ = Continue::try_from(elem).unwrap_err(); let continue_ = Continue::try_from(elem).unwrap_err();
let message = match continue_ { let message = match continue_ {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in continue element.".to_owned()); assert_eq!(message, "Unknown child in continue element.".to_owned());
@ -557,7 +561,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Reason::try_from(elem).unwrap_err(); let error = Reason::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in reason element.".to_owned()); assert_eq!(message, "Unknown attribute in reason element.".to_owned());
@ -573,7 +577,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Reason::try_from(elem).unwrap_err(); let error = Reason::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in reason element.".to_owned()); assert_eq!(message, "Unknown child in reason element.".to_owned());
@ -588,7 +592,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Item::try_from(elem).unwrap_err(); let error = Item::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in item element.".to_owned()); assert_eq!(message, "Unknown attribute in item element.".to_owned());
@ -612,7 +616,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Item::try_from(elem).unwrap_err(); let error = Item::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'role' missing.".to_owned()); assert_eq!(message, "Required attribute 'role' missing.".to_owned());
@ -640,7 +644,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Item::try_from(elem).unwrap_err(); let error = Item::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!( assert_eq!(

View file

@ -14,9 +14,9 @@ generate_elem_id!(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[cfg(not(feature = "disable-validation"))]
use crate::util::error::Error;
use crate::Element; use crate::Element;
#[cfg(not(feature = "disable-validation"))]
use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -56,7 +56,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Nick::try_from(elem).unwrap_err(); let error = Nick::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in nick element."); assert_eq!(message, "Unknown child in nick element.");
@ -70,7 +70,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Nick::try_from(elem).unwrap_err(); let error = Nick::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in nick element."); assert_eq!(message, "Unknown attribute in nick element.");

View file

@ -26,8 +26,8 @@ impl PresencePayload for OccupantId {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -57,7 +57,7 @@ mod tests {
.unwrap(); .unwrap();
let error = OccupantId::try_from(elem).unwrap_err(); let error = OccupantId::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in occupant-id element."); assert_eq!(message, "Unknown child in occupant-id element.");
@ -70,7 +70,7 @@ mod tests {
.unwrap(); .unwrap();
let error = OccupantId::try_from(elem).unwrap_err(); let error = OccupantId::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'id' missing."); assert_eq!(message, "Required attribute 'id' missing.");

View file

@ -22,8 +22,8 @@ impl MessagePayload for Oob {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -59,7 +59,7 @@ mod tests {
let elem: Element = "<x xmlns='jabber:x:oob'></x>".parse().unwrap(); let elem: Element = "<x xmlns='jabber:x:oob'></x>".parse().unwrap();
let error = Oob::try_from(elem).unwrap_err(); let error = Oob::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Missing child url in x element."); assert_eq!(message, "Missing child url in x element.");

View file

@ -20,9 +20,9 @@ impl IqGetPayload for Ping {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[cfg(not(feature = "disable-validation"))]
use crate::util::error::Error;
use crate::Element; use crate::Element;
#[cfg(not(feature = "disable-validation"))]
use xso::error::{Error, FromElementError};
#[test] #[test]
fn test_size() { fn test_size() {
@ -50,7 +50,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Ping::try_from(elem).unwrap_err(); let error = Ping::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in ping element."); assert_eq!(message, "Unknown child in ping element.");
@ -62,7 +62,7 @@ mod tests {
let elem: Element = "<ping xmlns='urn:xmpp:ping' coucou=''/>".parse().unwrap(); let elem: Element = "<ping xmlns='urn:xmpp:ping' coucou=''/>".parse().unwrap();
let error = Ping::try_from(elem).unwrap_err(); let error = Ping::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in ping element."); assert_eq!(message, "Unknown attribute in ping element.");

View file

@ -6,11 +6,11 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::ns; use crate::ns;
use crate::util::error::Error;
use jid::Jid; use jid::Jid;
use minidom::{Element, IntoAttributeValue}; use minidom::{Element, IntoAttributeValue};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::str::FromStr; use std::str::FromStr;
use xso::error::{Error, FromElementError};
/// Should be implemented on every known payload of a `<presence/>`. /// Should be implemented on every known payload of a `<presence/>`.
pub trait PresencePayload: TryFrom<Element> + Into<Element> {} pub trait PresencePayload: TryFrom<Element> + Into<Element> {}
@ -42,7 +42,7 @@ impl FromStr for Show {
"dnd" => Show::Dnd, "dnd" => Show::Dnd,
"xa" => Show::Xa, "xa" => Show::Xa,
_ => return Err(Error::ParseError("Invalid value for show.")), _ => return Err(Error::Other("Invalid value for show.").into()),
}) })
} }
} }
@ -114,9 +114,7 @@ impl FromStr for Type {
"unsubscribed" => Type::Unsubscribed, "unsubscribed" => Type::Unsubscribed,
_ => { _ => {
return Err(Error::ParseError( return Err(Error::Other("Invalid 'type' attribute on presence element.").into());
"Invalid 'type' attribute on presence element.",
));
} }
}) })
} }
@ -281,9 +279,9 @@ impl Presence {
} }
impl TryFrom<Element> for Presence { impl TryFrom<Element> for Presence {
type Error = Error; type Error = FromElementError;
fn try_from(root: Element) -> Result<Presence, Error> { fn try_from(root: Element) -> Result<Presence, FromElementError> {
check_self!(root, "presence", DEFAULT_NS); check_self!(root, "presence", DEFAULT_NS);
let mut show = None; let mut show = None;
let mut priority = None; let mut priority = None;
@ -300,9 +298,7 @@ impl TryFrom<Element> for Presence {
for elem in root.children() { for elem in root.children() {
if elem.is("show", ns::DEFAULT_NS) { if elem.is("show", ns::DEFAULT_NS) {
if show.is_some() { if show.is_some() {
return Err(Error::ParseError( return Err(Error::Other("More than one show element in a presence.").into());
"More than one show element in a presence.",
));
} }
check_no_attributes!(elem, "show"); check_no_attributes!(elem, "show");
check_no_children!(elem, "show"); check_no_children!(elem, "show");
@ -312,19 +308,22 @@ impl TryFrom<Element> for Presence {
check_no_children!(elem, "status"); check_no_children!(elem, "status");
let lang = get_attr!(elem, "xml:lang", Default); let lang = get_attr!(elem, "xml:lang", Default);
if presence.statuses.insert(lang, elem.text()).is_some() { if presence.statuses.insert(lang, elem.text()).is_some() {
return Err(Error::ParseError( return Err(Error::Other(
"Status element present twice for the same xml:lang.", "Status element present twice for the same xml:lang.",
)); )
.into());
} }
} else if elem.is("priority", ns::DEFAULT_NS) { } else if elem.is("priority", ns::DEFAULT_NS) {
if priority.is_some() { if priority.is_some() {
return Err(Error::ParseError( return Err(
"More than one priority element in a presence.", Error::Other("More than one priority element in a presence.").into(),
)); );
} }
check_no_attributes!(elem, "priority"); check_no_attributes!(elem, "priority");
check_no_children!(elem, "priority"); check_no_children!(elem, "priority");
priority = Some(Priority::from_str(elem.text().as_ref())?); priority = Some(
Priority::from_str(elem.text().as_ref()).map_err(Error::text_parse_error)?,
);
} else { } else {
presence.payloads.push(elem.clone()); presence.payloads.push(elem.clone());
} }
@ -461,7 +460,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Presence::try_from(elem).unwrap_err(); let error = Presence::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Invalid value for show."); assert_eq!(message, "Invalid value for show.");
@ -481,7 +480,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Presence::try_from(elem).unwrap_err(); let error = Presence::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Invalid value for show."); assert_eq!(message, "Invalid value for show.");
@ -541,7 +540,7 @@ mod tests {
let elem: Element = "<presence xmlns='jabber:component:accept'><status xml:lang='fr'>Here!</status><status xml:lang='fr'>Là!</status></presence>".parse().unwrap(); let elem: Element = "<presence xmlns='jabber:component:accept'><status xml:lang='fr'>Here!</status><status xml:lang='fr'>Là!</status></presence>".parse().unwrap();
let error = Presence::try_from(elem).unwrap_err(); let error = Presence::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!( assert_eq!(
@ -579,7 +578,11 @@ mod tests {
.unwrap(); .unwrap();
let error = Presence::try_from(elem).unwrap_err(); let error = Presence::try_from(elem).unwrap_err();
match error { match error {
Error::ParseIntError(_) => (), FromElementError::Invalid(Error::TextParseError(e))
if e.is::<std::num::ParseIntError>() =>
{
()
}
_ => panic!(), _ => panic!(),
}; };
} }
@ -614,7 +617,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Presence::try_from(elem).unwrap_err(); let error = Presence::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in status element."); assert_eq!(message, "Unknown child in status element.");
@ -634,7 +637,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Presence::try_from(elem).unwrap_err(); let error = Presence::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in status element."); assert_eq!(message, "Unknown attribute in status element.");

View file

@ -9,9 +9,9 @@ use crate::date::DateTime;
use crate::message::MessagePayload; use crate::message::MessagePayload;
use crate::ns; use crate::ns;
use crate::pubsub::{Item as PubSubItem, ItemId, NodeName, Subscription, SubscriptionId}; use crate::pubsub::{Item as PubSubItem, ItemId, NodeName, Subscription, SubscriptionId};
use crate::util::error::Error;
use crate::Element; use crate::Element;
use jid::Jid; use jid::Jid;
use xso::error::{Error, FromElementError};
/// Event wrapper for a PubSub `<item/>`. /// Event wrapper for a PubSub `<item/>`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -97,9 +97,7 @@ fn parse_items(elem: Element, node: NodeName) -> Result<PubSubEvent, Error> {
None => is_retract = Some(false), None => is_retract = Some(false),
Some(false) => (), Some(false) => (),
Some(true) => { Some(true) => {
return Err(Error::ParseError( return Err(Error::Other("Mix of item and retract in items element.").into());
"Mix of item and retract in items element.",
));
} }
} }
items.push(Item::try_from(child.clone())?); items.push(Item::try_from(child.clone())?);
@ -108,9 +106,7 @@ fn parse_items(elem: Element, node: NodeName) -> Result<PubSubEvent, Error> {
None => is_retract = Some(true), None => is_retract = Some(true),
Some(true) => (), Some(true) => (),
Some(false) => { Some(false) => {
return Err(Error::ParseError( return Err(Error::Other("Mix of item and retract in items element.").into());
"Mix of item and retract in items element.",
));
} }
} }
check_no_children!(child, "retract"); check_no_children!(child, "retract");
@ -118,7 +114,7 @@ fn parse_items(elem: Element, node: NodeName) -> Result<PubSubEvent, Error> {
let id = get_attr!(child, "id", Required); let id = get_attr!(child, "id", Required);
retracts.push(id); retracts.push(id);
} else { } else {
return Err(Error::ParseError("Invalid child in items element.")); return Err(Error::Other("Invalid child in items element.").into());
} }
} }
Ok(match is_retract { Ok(match is_retract {
@ -127,14 +123,14 @@ fn parse_items(elem: Element, node: NodeName) -> Result<PubSubEvent, Error> {
node, node,
items: retracts, items: retracts,
}, },
None => return Err(Error::ParseError("Missing children in items element.")), None => return Err(Error::Other("Missing children in items element.").into()),
}) })
} }
impl TryFrom<Element> for PubSubEvent { impl TryFrom<Element> for PubSubEvent {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<PubSubEvent, Error> { fn try_from(elem: Element) -> Result<PubSubEvent, FromElementError> {
check_self!(elem, "event", PUBSUB_EVENT); check_self!(elem, "event", PUBSUB_EVENT);
check_no_attributes!(elem, "event"); check_no_attributes!(elem, "event");
@ -145,9 +141,10 @@ impl TryFrom<Element> for PubSubEvent {
let mut payloads = child.children().cloned().collect::<Vec<_>>(); let mut payloads = child.children().cloned().collect::<Vec<_>>();
let item = payloads.pop(); let item = payloads.pop();
if !payloads.is_empty() { if !payloads.is_empty() {
return Err(Error::ParseError( return Err(Error::Other(
"More than a single payload in configuration element.", "More than a single payload in configuration element.",
)); )
.into());
} }
let form = match item { let form = match item {
None => None, None => None,
@ -159,14 +156,14 @@ impl TryFrom<Element> for PubSubEvent {
for item in child.children() { for item in child.children() {
if item.is("redirect", ns::PUBSUB_EVENT) { if item.is("redirect", ns::PUBSUB_EVENT) {
if redirect.is_some() { if redirect.is_some() {
return Err(Error::ParseError( return Err(
"More than one redirect in delete element.", Error::Other("More than one redirect in delete element.").into()
)); );
} }
let uri = get_attr!(item, "uri", Required); let uri = get_attr!(item, "uri", Required);
redirect = Some(uri); redirect = Some(uri);
} else { } else {
return Err(Error::ParseError("Unknown child in delete element.")); return Err(Error::Other("Unknown child in delete element.").into());
} }
} }
payload = Some(PubSubEvent::Delete { node, redirect }); payload = Some(PubSubEvent::Delete { node, redirect });
@ -185,10 +182,10 @@ impl TryFrom<Element> for PubSubEvent {
subscription: get_attr!(child, "subscription", Option), subscription: get_attr!(child, "subscription", Option),
}); });
} else { } else {
return Err(Error::ParseError("Unknown child in event element.")); return Err(Error::Other("Unknown child in event element.").into());
} }
} }
payload.ok_or(Error::ParseError("No payload in event element.")) payload.ok_or(Error::Other("No payload in event element.").into())
} }
} }
@ -270,7 +267,7 @@ mod tests {
.unwrap(); .unwrap();
let error = PubSubEvent::try_from(elem).unwrap_err(); let error = PubSubEvent::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Missing children in items element."); assert_eq!(message, "Missing children in items element.");
@ -375,7 +372,7 @@ mod tests {
.unwrap(); .unwrap();
let error = PubSubEvent::try_from(elem).unwrap_err(); let error = PubSubEvent::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in event element."); assert_eq!(message, "Unknown child in event element.");
@ -389,7 +386,7 @@ mod tests {
.unwrap(); .unwrap();
let error = PubSubEvent::try_from(elem).unwrap_err(); let error = PubSubEvent::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in event element."); assert_eq!(message, "Unknown attribute in event element.");

View file

@ -9,9 +9,9 @@ use crate::data_forms::DataForm;
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
use crate::ns; use crate::ns;
use crate::pubsub::{AffiliationAttribute, NodeName, Subscription}; use crate::pubsub::{AffiliationAttribute, NodeName, Subscription};
use crate::util::error::Error;
use crate::Element; use crate::Element;
use jid::Jid; use jid::Jid;
use xso::error::{Error, FromElementError};
generate_element!( generate_element!(
/// A list of affiliations you have on a service, or on a node. /// A list of affiliations you have on a service, or on a node.
@ -143,9 +143,9 @@ impl IqSetPayload for PubSubOwner {}
impl IqResultPayload for PubSubOwner {} impl IqResultPayload for PubSubOwner {}
impl TryFrom<Element> for PubSubOwner { impl TryFrom<Element> for PubSubOwner {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<PubSubOwner, Error> { fn try_from(elem: Element) -> Result<PubSubOwner, FromElementError> {
check_self!(elem, "pubsub", PUBSUB_OWNER); check_self!(elem, "pubsub", PUBSUB_OWNER);
check_no_attributes!(elem, "pubsub"); check_no_attributes!(elem, "pubsub");
@ -153,17 +153,18 @@ impl TryFrom<Element> for PubSubOwner {
for child in elem.children() { for child in elem.children() {
if child.is("configure", ns::PUBSUB_OWNER) { if child.is("configure", ns::PUBSUB_OWNER) {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError( return Err(Error::Other(
"Payload is already defined in pubsub owner element.", "Payload is already defined in pubsub owner element.",
)); )
.into());
} }
let configure = Configure::try_from(child.clone())?; let configure = Configure::try_from(child.clone())?;
payload = Some(PubSubOwner::Configure(configure)); payload = Some(PubSubOwner::Configure(configure));
} else { } else {
return Err(Error::ParseError("Unknown child in pubsub element.")); return Err(Error::Other("Unknown child in pubsub element.").into());
} }
} }
payload.ok_or(Error::ParseError("No payload in pubsub element.")) payload.ok_or(Error::Other("No payload in pubsub element.").into())
} }
} }

View file

@ -10,9 +10,9 @@ use crate::ns;
use crate::pubsub::{ use crate::pubsub::{
AffiliationAttribute, Item as PubSubItem, NodeName, Subscription, SubscriptionId, AffiliationAttribute, Item as PubSubItem, NodeName, Subscription, SubscriptionId,
}; };
use crate::util::error::Error;
use crate::Element; use crate::Element;
use jid::Jid; use jid::Jid;
use xso::error::{Error, FromElementError};
// TODO: a better solution would be to split this into a query and a result elements, like for // TODO: a better solution would be to split this into a query and a result elements, like for
// XEP-0030. // XEP-0030.
@ -181,24 +181,23 @@ pub struct SubscribeOptions {
} }
impl TryFrom<Element> for SubscribeOptions { impl TryFrom<Element> for SubscribeOptions {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<Self, Error> { fn try_from(elem: Element) -> Result<Self, FromElementError> {
check_self!(elem, "subscribe-options", PUBSUB); check_self!(elem, "subscribe-options", PUBSUB);
check_no_attributes!(elem, "subscribe-options"); check_no_attributes!(elem, "subscribe-options");
let mut required = false; let mut required = false;
for child in elem.children() { for child in elem.children() {
if child.is("required", ns::PUBSUB) { if child.is("required", ns::PUBSUB) {
if required { if required {
return Err(Error::ParseError( return Err(Error::Other(
"More than one required element in subscribe-options.", "More than one required element in subscribe-options.",
)); )
.into());
} }
required = true; required = true;
} else { } else {
return Err(Error::ParseError( return Err(Error::Other("Unknown child in subscribe-options element.").into());
"Unknown child in subscribe-options element.",
));
} }
} }
Ok(SubscribeOptions { required }) Ok(SubscribeOptions { required })
@ -338,9 +337,9 @@ impl IqSetPayload for PubSub {}
impl IqResultPayload for PubSub {} impl IqResultPayload for PubSub {}
impl TryFrom<Element> for PubSub { impl TryFrom<Element> for PubSub {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<PubSub, Error> { fn try_from(elem: Element) -> Result<PubSub, FromElementError> {
check_self!(elem, "pubsub", PUBSUB); check_self!(elem, "pubsub", PUBSUB);
check_no_attributes!(elem, "pubsub"); check_no_attributes!(elem, "pubsub");
@ -348,9 +347,9 @@ impl TryFrom<Element> for PubSub {
for child in elem.children() { for child in elem.children() {
if child.is("create", ns::PUBSUB) { if child.is("create", ns::PUBSUB) {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError( return Err(
"Payload is already defined in pubsub element.", Error::Other("Payload is already defined in pubsub element.").into(),
)); );
} }
let create = Create::try_from(child.clone())?; let create = Create::try_from(child.clone())?;
payload = Some(PubSub::Create { payload = Some(PubSub::Create {
@ -359,9 +358,9 @@ impl TryFrom<Element> for PubSub {
}); });
} else if child.is("subscribe", ns::PUBSUB) { } else if child.is("subscribe", ns::PUBSUB) {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError( return Err(
"Payload is already defined in pubsub element.", Error::Other("Payload is already defined in pubsub element.").into(),
)); );
} }
let subscribe = Subscribe::try_from(child.clone())?; let subscribe = Subscribe::try_from(child.clone())?;
payload = Some(PubSub::Subscribe { payload = Some(PubSub::Subscribe {
@ -371,9 +370,9 @@ impl TryFrom<Element> for PubSub {
} else if child.is("options", ns::PUBSUB) { } else if child.is("options", ns::PUBSUB) {
if let Some(PubSub::Subscribe { subscribe, options }) = payload { if let Some(PubSub::Subscribe { subscribe, options }) = payload {
if options.is_some() { if options.is_some() {
return Err(Error::ParseError( return Err(
"Options is already defined in pubsub element.", Error::Other("Options is already defined in pubsub element.").into(),
)); );
} }
let options = Some(Options::try_from(child.clone())?); let options = Some(Options::try_from(child.clone())?);
payload = Some(PubSub::Subscribe { subscribe, options }); payload = Some(PubSub::Subscribe { subscribe, options });
@ -384,29 +383,30 @@ impl TryFrom<Element> for PubSub {
options: Some(options), options: Some(options),
}); });
} else { } else {
return Err(Error::ParseError( return Err(
"Payload is already defined in pubsub element.", Error::Other("Payload is already defined in pubsub element.").into(),
)); );
} }
} else if child.is("configure", ns::PUBSUB) { } else if child.is("configure", ns::PUBSUB) {
if let Some(PubSub::Create { create, configure }) = payload { if let Some(PubSub::Create { create, configure }) = payload {
if configure.is_some() { if configure.is_some() {
return Err(Error::ParseError( return Err(Error::Other(
"Configure is already defined in pubsub element.", "Configure is already defined in pubsub element.",
)); )
.into());
} }
let configure = Some(Configure::try_from(child.clone())?); let configure = Some(Configure::try_from(child.clone())?);
payload = Some(PubSub::Create { create, configure }); payload = Some(PubSub::Create { create, configure });
} else { } else {
return Err(Error::ParseError( return Err(
"Payload is already defined in pubsub element.", Error::Other("Payload is already defined in pubsub element.").into(),
)); );
} }
} else if child.is("publish", ns::PUBSUB) { } else if child.is("publish", ns::PUBSUB) {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError( return Err(
"Payload is already defined in pubsub element.", Error::Other("Payload is already defined in pubsub element.").into(),
)); );
} }
let publish = Publish::try_from(child.clone())?; let publish = Publish::try_from(child.clone())?;
payload = Some(PubSub::Publish { payload = Some(PubSub::Publish {
@ -420,9 +420,10 @@ impl TryFrom<Element> for PubSub {
}) = payload }) = payload
{ {
if publish_options.is_some() { if publish_options.is_some() {
return Err(Error::ParseError( return Err(Error::Other(
"Publish-options are already defined in pubsub element.", "Publish-options are already defined in pubsub element.",
)); )
.into());
} }
let publish_options = Some(PublishOptions::try_from(child.clone())?); let publish_options = Some(PublishOptions::try_from(child.clone())?);
payload = Some(PubSub::Publish { payload = Some(PubSub::Publish {
@ -430,71 +431,71 @@ impl TryFrom<Element> for PubSub {
publish_options, publish_options,
}); });
} else { } else {
return Err(Error::ParseError( return Err(
"Payload is already defined in pubsub element.", Error::Other("Payload is already defined in pubsub element.").into(),
)); );
} }
} else if child.is("affiliations", ns::PUBSUB) { } else if child.is("affiliations", ns::PUBSUB) {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError( return Err(
"Payload is already defined in pubsub element.", Error::Other("Payload is already defined in pubsub element.").into(),
)); );
} }
let affiliations = Affiliations::try_from(child.clone())?; let affiliations = Affiliations::try_from(child.clone())?;
payload = Some(PubSub::Affiliations(affiliations)); payload = Some(PubSub::Affiliations(affiliations));
} else if child.is("default", ns::PUBSUB) { } else if child.is("default", ns::PUBSUB) {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError( return Err(
"Payload is already defined in pubsub element.", Error::Other("Payload is already defined in pubsub element.").into(),
)); );
} }
let default = Default::try_from(child.clone())?; let default = Default::try_from(child.clone())?;
payload = Some(PubSub::Default(default)); payload = Some(PubSub::Default(default));
} else if child.is("items", ns::PUBSUB) { } else if child.is("items", ns::PUBSUB) {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError( return Err(
"Payload is already defined in pubsub element.", Error::Other("Payload is already defined in pubsub element.").into(),
)); );
} }
let items = Items::try_from(child.clone())?; let items = Items::try_from(child.clone())?;
payload = Some(PubSub::Items(items)); payload = Some(PubSub::Items(items));
} else if child.is("retract", ns::PUBSUB) { } else if child.is("retract", ns::PUBSUB) {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError( return Err(
"Payload is already defined in pubsub element.", Error::Other("Payload is already defined in pubsub element.").into(),
)); );
} }
let retract = Retract::try_from(child.clone())?; let retract = Retract::try_from(child.clone())?;
payload = Some(PubSub::Retract(retract)); payload = Some(PubSub::Retract(retract));
} else if child.is("subscription", ns::PUBSUB) { } else if child.is("subscription", ns::PUBSUB) {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError( return Err(
"Payload is already defined in pubsub element.", Error::Other("Payload is already defined in pubsub element.").into(),
)); );
} }
let subscription = SubscriptionElem::try_from(child.clone())?; let subscription = SubscriptionElem::try_from(child.clone())?;
payload = Some(PubSub::Subscription(subscription)); payload = Some(PubSub::Subscription(subscription));
} else if child.is("subscriptions", ns::PUBSUB) { } else if child.is("subscriptions", ns::PUBSUB) {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError( return Err(
"Payload is already defined in pubsub element.", Error::Other("Payload is already defined in pubsub element.").into(),
)); );
} }
let subscriptions = Subscriptions::try_from(child.clone())?; let subscriptions = Subscriptions::try_from(child.clone())?;
payload = Some(PubSub::Subscriptions(subscriptions)); payload = Some(PubSub::Subscriptions(subscriptions));
} else if child.is("unsubscribe", ns::PUBSUB) { } else if child.is("unsubscribe", ns::PUBSUB) {
if payload.is_some() { if payload.is_some() {
return Err(Error::ParseError( return Err(
"Payload is already defined in pubsub element.", Error::Other("Payload is already defined in pubsub element.").into(),
)); );
} }
let unsubscribe = Unsubscribe::try_from(child.clone())?; let unsubscribe = Unsubscribe::try_from(child.clone())?;
payload = Some(PubSub::Unsubscribe(unsubscribe)); payload = Some(PubSub::Unsubscribe(unsubscribe));
} else { } else {
return Err(Error::ParseError("Unknown child in pubsub element.")); return Err(Error::Other("Unknown child in pubsub element.").into());
} }
} }
payload.ok_or(Error::ParseError("No payload in pubsub element.")) payload.ok_or(Error::Other("No payload in pubsub element.").into())
} }
} }
@ -678,7 +679,7 @@ mod tests {
.unwrap(); .unwrap();
let error = PubSub::try_from(elem).unwrap_err(); let error = PubSub::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "No payload in pubsub element."); assert_eq!(message, "No payload in pubsub element.");

View file

@ -32,8 +32,8 @@ impl MessagePayload for Received {}
mod tests { mod tests {
use super::*; use super::*;
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -65,7 +65,7 @@ mod tests {
let elem: Element = "<received xmlns='urn:xmpp:receipts'/>".parse().unwrap(); let elem: Element = "<received xmlns='urn:xmpp:receipts'/>".parse().unwrap();
let error = Received::try_from(elem).unwrap_err(); let error = Received::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'id' missing."); assert_eq!(message, "Required attribute 'id' missing.");

View file

@ -91,9 +91,9 @@ impl IqResultPayload for Roster {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use std::str::FromStr; use std::str::FromStr;
use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -270,7 +270,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Roster::try_from(elem).unwrap_err(); let error = Roster::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in query element."); assert_eq!(message, "Unknown child in query element.");
@ -280,7 +280,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Roster::try_from(elem).unwrap_err(); let error = Roster::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in query element."); assert_eq!(message, "Unknown attribute in query element.");
@ -293,7 +293,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Roster::try_from(elem).unwrap_err(); let error = Roster::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'jid' missing."); assert_eq!(message, "Required attribute 'jid' missing.");
@ -314,7 +314,7 @@ mod tests {
.unwrap(); .unwrap();
let error = Roster::try_from(elem).unwrap_err(); let error = Roster::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in item element."); assert_eq!(message, "Unknown child in item element.");

View file

@ -5,8 +5,8 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
/// Requests paging through a potentially big set of items (represented by an /// Requests paging through a potentially big set of items (represented by an
/// UID). /// UID).
@ -28,9 +28,9 @@ pub struct SetQuery {
} }
impl TryFrom<Element> for SetQuery { impl TryFrom<Element> for SetQuery {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<SetQuery, Error> { fn try_from(elem: Element) -> Result<SetQuery, FromElementError> {
check_self!(elem, "set", RSM, "RSM set"); check_self!(elem, "set", RSM, "RSM set");
let mut set = SetQuery { let mut set = SetQuery {
max: None, max: None,
@ -41,26 +41,26 @@ impl TryFrom<Element> for SetQuery {
for child in elem.children() { for child in elem.children() {
if child.is("max", ns::RSM) { if child.is("max", ns::RSM) {
if set.max.is_some() { if set.max.is_some() {
return Err(Error::ParseError("Set cant have more than one max.")); return Err(Error::Other("Set cant have more than one max.").into());
} }
set.max = Some(child.text().parse()?); set.max = Some(child.text().parse().map_err(Error::text_parse_error)?);
} else if child.is("after", ns::RSM) { } else if child.is("after", ns::RSM) {
if set.after.is_some() { if set.after.is_some() {
return Err(Error::ParseError("Set cant have more than one after.")); return Err(Error::Other("Set cant have more than one after.").into());
} }
set.after = Some(child.text()); set.after = Some(child.text());
} else if child.is("before", ns::RSM) { } else if child.is("before", ns::RSM) {
if set.before.is_some() { if set.before.is_some() {
return Err(Error::ParseError("Set cant have more than one before.")); return Err(Error::Other("Set cant have more than one before.").into());
} }
set.before = Some(child.text()); set.before = Some(child.text());
} else if child.is("index", ns::RSM) { } else if child.is("index", ns::RSM) {
if set.index.is_some() { if set.index.is_some() {
return Err(Error::ParseError("Set cant have more than one index.")); return Err(Error::Other("Set cant have more than one index.").into());
} }
set.index = Some(child.text().parse()?); set.index = Some(child.text().parse().map_err(Error::text_parse_error)?);
} else { } else {
return Err(Error::ParseError("Unknown child in set element.")); return Err(Error::Other("Unknown child in set element.").into());
} }
} }
Ok(set) Ok(set)
@ -111,9 +111,9 @@ pub struct SetResult {
} }
impl TryFrom<Element> for SetResult { impl TryFrom<Element> for SetResult {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<SetResult, Error> { fn try_from(elem: Element) -> Result<SetResult, FromElementError> {
check_self!(elem, "set", RSM, "RSM set"); check_self!(elem, "set", RSM, "RSM set");
let mut set = SetResult { let mut set = SetResult {
first: None, first: None,
@ -124,22 +124,22 @@ impl TryFrom<Element> for SetResult {
for child in elem.children() { for child in elem.children() {
if child.is("first", ns::RSM) { if child.is("first", ns::RSM) {
if set.first.is_some() { if set.first.is_some() {
return Err(Error::ParseError("Set cant have more than one first.")); return Err(Error::Other("Set cant have more than one first.").into());
} }
set.first_index = get_attr!(child, "index", Option); set.first_index = get_attr!(child, "index", Option);
set.first = Some(child.text()); set.first = Some(child.text());
} else if child.is("last", ns::RSM) { } else if child.is("last", ns::RSM) {
if set.last.is_some() { if set.last.is_some() {
return Err(Error::ParseError("Set cant have more than one last.")); return Err(Error::Other("Set cant have more than one last.").into());
} }
set.last = Some(child.text()); set.last = Some(child.text());
} else if child.is("count", ns::RSM) { } else if child.is("count", ns::RSM) {
if set.count.is_some() { if set.count.is_some() {
return Err(Error::ParseError("Set cant have more than one count.")); return Err(Error::Other("Set cant have more than one count.").into());
} }
set.count = Some(child.text().parse()?); set.count = Some(child.text().parse().map_err(Error::text_parse_error)?);
} else { } else {
return Err(Error::ParseError("Unknown child in set element.")); return Err(Error::Other("Unknown child in set element.").into());
} }
} }
Ok(set) Ok(set)
@ -215,7 +215,7 @@ mod tests {
.unwrap(); .unwrap();
let error = SetQuery::try_from(elem.clone()).unwrap_err(); let error = SetQuery::try_from(elem.clone()).unwrap_err();
let returned_elem = match error { let returned_elem = match error {
Error::TypeMismatch(_, _, elem) => elem, FromElementError::Mismatch(elem) => elem,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(elem, returned_elem); assert_eq!(elem, returned_elem);
@ -225,7 +225,7 @@ mod tests {
.unwrap(); .unwrap();
let error = SetResult::try_from(elem.clone()).unwrap_err(); let error = SetResult::try_from(elem.clone()).unwrap_err();
let returned_elem = match error { let returned_elem = match error {
Error::TypeMismatch(_, _, elem) => elem, FromElementError::Mismatch(elem) => elem,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(elem, returned_elem); assert_eq!(elem, returned_elem);
@ -238,7 +238,7 @@ mod tests {
.unwrap(); .unwrap();
let error = SetQuery::try_from(elem).unwrap_err(); let error = SetQuery::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in set element."); assert_eq!(message, "Unknown child in set element.");
@ -248,7 +248,7 @@ mod tests {
.unwrap(); .unwrap();
let error = SetResult::try_from(elem).unwrap_err(); let error = SetResult::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in set element."); assert_eq!(message, "Unknown child in set element.");

View file

@ -5,9 +5,9 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::util::text_node_codecs::{Codec, OptionalCodec, Text}; use crate::util::text_node_codecs::{Codec, OptionalCodec, Text};
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
generate_attribute!( generate_attribute!(
/// Events for real-time text. /// Events for real-time text.
@ -49,7 +49,7 @@ impl TryFrom<Action> for Insert {
fn try_from(action: Action) -> Result<Insert, Error> { fn try_from(action: Action) -> Result<Insert, Error> {
match action { match action {
Action::Insert(insert) => Ok(insert), Action::Insert(insert) => Ok(insert),
_ => Err(Error::ParseError("This is not an insert action.")), _ => Err(Error::Other("This is not an insert action.")),
} }
} }
} }
@ -79,8 +79,8 @@ pub struct Erase {
} }
impl TryFrom<Element> for Erase { impl TryFrom<Element> for Erase {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<Erase, Error> { fn try_from(elem: Element) -> Result<Erase, FromElementError> {
check_self!(elem, "e", RTT); check_self!(elem, "e", RTT);
check_no_unknown_attributes!(elem, "e", ["p", "n"]); check_no_unknown_attributes!(elem, "e", ["p", "n"]);
let pos = get_attr!(elem, "p", Option); let pos = get_attr!(elem, "p", Option);
@ -105,7 +105,7 @@ impl TryFrom<Action> for Erase {
fn try_from(action: Action) -> Result<Erase, Error> { fn try_from(action: Action) -> Result<Erase, Error> {
match action { match action {
Action::Erase(erase) => Ok(erase), Action::Erase(erase) => Ok(erase),
_ => Err(Error::ParseError("This is not an erase action.")), _ => Err(Error::Other("This is not an erase action.")),
} }
} }
} }
@ -127,7 +127,7 @@ impl TryFrom<Action> for Wait {
fn try_from(action: Action) -> Result<Wait, Error> { fn try_from(action: Action) -> Result<Wait, Error> {
match action { match action {
Action::Wait(wait) => Ok(wait), Action::Wait(wait) => Ok(wait),
_ => Err(Error::ParseError("This is not a wait action.")), _ => Err(Error::Other("This is not a wait action.")),
} }
} }
} }
@ -146,14 +146,14 @@ pub enum Action {
} }
impl TryFrom<Element> for Action { impl TryFrom<Element> for Action {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<Action, Error> { fn try_from(elem: Element) -> Result<Action, FromElementError> {
match elem.name() { match elem.name() {
"t" => Insert::try_from(elem).map(Action::Insert), "t" => Insert::try_from(elem).map(Action::Insert),
"e" => Erase::try_from(elem).map(Action::Erase), "e" => Erase::try_from(elem).map(Action::Erase),
"w" => Wait::try_from(elem).map(Action::Wait), "w" => Wait::try_from(elem).map(Action::Wait),
_ => Err(Error::ParseError("This is not a rtt action element.")), _ => Err(FromElementError::Mismatch(elem)),
} }
} }
} }
@ -204,8 +204,8 @@ pub struct Rtt {
} }
impl TryFrom<Element> for Rtt { impl TryFrom<Element> for Rtt {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<Rtt, Error> { fn try_from(elem: Element) -> Result<Rtt, FromElementError> {
check_self!(elem, "rtt", RTT); check_self!(elem, "rtt", RTT);
check_no_unknown_attributes!(elem, "rtt", ["seq", "event", "id"]); check_no_unknown_attributes!(elem, "rtt", ["seq", "event", "id"]);
@ -216,7 +216,7 @@ impl TryFrom<Element> for Rtt {
let mut actions = Vec::new(); let mut actions = Vec::new();
for child in elem.children() { for child in elem.children() {
if child.ns() != ns::RTT { if child.ns() != ns::RTT {
return Err(Error::ParseError("Unknown child in rtt element.")); return Err(Error::Other("Unknown child in rtt element.").into());
} }
actions.push(Action::try_from(child.clone())?); actions.push(Action::try_from(child.clone())?);
} }

View file

@ -5,10 +5,10 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::util::text_node_codecs::{Base64, Codec}; use crate::util::text_node_codecs::{Base64, Codec};
use crate::Element; use crate::Element;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use xso::error::{Error, FromElementError};
generate_attribute!( generate_attribute!(
/// The list of available SASL mechanisms. /// The list of available SASL mechanisms.
@ -150,9 +150,9 @@ pub struct Failure {
} }
impl TryFrom<Element> for Failure { impl TryFrom<Element> for Failure {
type Error = Error; type Error = FromElementError;
fn try_from(root: Element) -> Result<Failure, Error> { fn try_from(root: Element) -> Result<Failure, FromElementError> {
check_self!(root, "failure", SASL); check_self!(root, "failure", SASL);
check_no_attributes!(root, "failure"); check_no_attributes!(root, "failure");
@ -165,15 +165,17 @@ impl TryFrom<Element> for Failure {
check_no_children!(child, "text"); check_no_children!(child, "text");
let lang = get_attr!(child, "xml:lang", Default); let lang = get_attr!(child, "xml:lang", Default);
if texts.insert(lang, child.text()).is_some() { if texts.insert(lang, child.text()).is_some() {
return Err(Error::ParseError( return Err(Error::Other(
"Text element present twice for the same xml:lang in failure element.", "Text element present twice for the same xml:lang in failure element.",
)); )
.into());
} }
} else if child.has_ns(ns::SASL) { } else if child.has_ns(ns::SASL) {
if defined_condition.is_some() { if defined_condition.is_some() {
return Err(Error::ParseError( return Err(Error::Other(
"Failure must not have more than one defined-condition.", "Failure must not have more than one defined-condition.",
)); )
.into());
} }
check_no_attributes!(child, "defined-condition"); check_no_attributes!(child, "defined-condition");
check_no_children!(child, "defined-condition"); check_no_children!(child, "defined-condition");
@ -184,11 +186,11 @@ impl TryFrom<Element> for Failure {
}; };
defined_condition = Some(condition); defined_condition = Some(condition);
} else { } else {
return Err(Error::ParseError("Unknown element in Failure.")); return Err(Error::Other("Unknown element in Failure.").into());
} }
} }
let defined_condition = let defined_condition =
defined_condition.ok_or(Error::ParseError("Failure must have a defined-condition."))?; defined_condition.ok_or(Error::Other("Failure must have a defined-condition."))?;
Ok(Failure { Ok(Failure {
defined_condition, defined_condition,

View file

@ -5,7 +5,7 @@
use crate::data_forms::{DataForm, DataFormType, Field, FieldType}; use crate::data_forms::{DataForm, DataFormType, Field, FieldType};
use crate::ns; use crate::ns;
use crate::util::error::Error; use xso::error::Error;
/// Structure representing a `http://jabber.org/network/serverinfo` form type. /// Structure representing a `http://jabber.org/network/serverinfo` form type.
#[derive(Debug, Clone, PartialEq, Default)] #[derive(Debug, Clone, PartialEq, Default)]
@ -34,15 +34,15 @@ impl TryFrom<DataForm> for ServerInfo {
fn try_from(form: DataForm) -> Result<ServerInfo, Error> { fn try_from(form: DataForm) -> Result<ServerInfo, Error> {
if form.type_ != DataFormType::Result_ { if form.type_ != DataFormType::Result_ {
return Err(Error::ParseError("Wrong type of form.")); return Err(Error::Other("Wrong type of form."));
} }
if form.form_type != Some(String::from(ns::SERVER_INFO)) { if form.form_type != Some(String::from(ns::SERVER_INFO)) {
return Err(Error::ParseError("Wrong FORM_TYPE for form.")); return Err(Error::Other("Wrong FORM_TYPE for form."));
} }
let mut server_info = ServerInfo::default(); let mut server_info = ServerInfo::default();
for field in form.fields { for field in form.fields {
if field.type_ != FieldType::ListMulti { if field.type_ != FieldType::ListMulti {
return Err(Error::ParseError("Field is not of the required type.")); return Err(Error::Other("Field is not of the required type."));
} }
if field.var.as_deref() == Some("abuse-addresses") { if field.var.as_deref() == Some("abuse-addresses") {
server_info.abuse = field.values; server_info.abuse = field.values;
@ -57,7 +57,7 @@ impl TryFrom<DataForm> for ServerInfo {
} else if field.var.as_deref() == Some("support-addresses") { } else if field.var.as_deref() == Some("support-addresses") {
server_info.support = field.values; server_info.support = field.values;
} else { } else {
return Err(Error::ParseError("Unknown form field var.")); return Err(Error::Other("Unknown form field var."));
} }
} }

View file

@ -7,12 +7,12 @@
use crate::message::MessagePayload; use crate::message::MessagePayload;
use crate::ns; use crate::ns;
use crate::presence::PresencePayload; use crate::presence::PresencePayload;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use jid::Jid; use jid::Jid;
use minidom::Node; use minidom::Node;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use xso::error::{Error, FromElementError};
generate_attribute!( generate_attribute!(
/// The type of the error. /// The type of the error.
@ -249,9 +249,9 @@ impl StanzaError {
} }
impl TryFrom<Element> for StanzaError { impl TryFrom<Element> for StanzaError {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<StanzaError, Error> { fn try_from(elem: Element) -> Result<StanzaError, FromElementError> {
check_self!(elem, "error", DEFAULT_NS); check_self!(elem, "error", DEFAULT_NS);
// The code attribute has been deprecated in [XEP-0086](https://xmpp.org/extensions/xep-0086.html) // The code attribute has been deprecated in [XEP-0086](https://xmpp.org/extensions/xep-0086.html)
// which was deprecated in 2007. We don't error when it's here, but don't include it in the final struct. // which was deprecated in 2007. We don't error when it's here, but don't include it in the final struct.
@ -273,15 +273,16 @@ impl TryFrom<Element> for StanzaError {
check_no_unknown_attributes!(child, "text", ["xml:lang"]); check_no_unknown_attributes!(child, "text", ["xml:lang"]);
let lang = get_attr!(child, "xml:lang", Default); let lang = get_attr!(child, "xml:lang", Default);
if stanza_error.texts.insert(lang, child.text()).is_some() { if stanza_error.texts.insert(lang, child.text()).is_some() {
return Err(Error::ParseError( return Err(
"Text element present twice for the same xml:lang.", Error::Other("Text element present twice for the same xml:lang.").into(),
)); );
} }
} else if child.has_ns(ns::XMPP_STANZAS) { } else if child.has_ns(ns::XMPP_STANZAS) {
if defined_condition.is_some() { if defined_condition.is_some() {
return Err(Error::ParseError( return Err(Error::Other(
"Error must not have more than one defined-condition.", "Error must not have more than one defined-condition.",
)); )
.into());
} }
check_no_children!(child, "defined-condition"); check_no_children!(child, "defined-condition");
check_no_attributes!(child, "defined-condition"); check_no_attributes!(child, "defined-condition");
@ -297,15 +298,15 @@ impl TryFrom<Element> for StanzaError {
defined_condition = Some(condition); defined_condition = Some(condition);
} else { } else {
if stanza_error.other.is_some() { if stanza_error.other.is_some() {
return Err(Error::ParseError( return Err(
"Error must not have more than one other element.", Error::Other("Error must not have more than one other element.").into(),
)); );
} }
stanza_error.other = Some(child.clone()); stanza_error.other = Some(child.clone());
} }
} }
stanza_error.defined_condition = stanza_error.defined_condition =
defined_condition.ok_or(Error::ParseError("Error must have a defined-condition."))?; defined_condition.ok_or(Error::Other("Error must have a defined-condition."))?;
Ok(stanza_error) Ok(stanza_error)
} }
@ -369,7 +370,7 @@ mod tests {
let elem: Element = "<error xmlns='jabber:component:accept'/>".parse().unwrap(); let elem: Element = "<error xmlns='jabber:component:accept'/>".parse().unwrap();
let error = StanzaError::try_from(elem).unwrap_err(); let error = StanzaError::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'type' missing."); assert_eq!(message, "Required attribute 'type' missing.");
@ -384,10 +385,10 @@ mod tests {
.unwrap(); .unwrap();
let error = StanzaError::try_from(elem).unwrap_err(); let error = StanzaError::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::TextParseError(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown value for 'type' attribute."); assert_eq!(message.to_string(), "Unknown value for 'type' attribute.");
} }
#[test] #[test]
@ -402,7 +403,7 @@ mod tests {
.unwrap(); .unwrap();
let error = StanzaError::try_from(elem).unwrap_err(); let error = StanzaError::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Error must have a defined-condition."); assert_eq!(message, "Error must have a defined-condition.");

View file

@ -37,9 +37,9 @@ impl MessagePayload for OriginId {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use jid::BareJid; use jid::BareJid;
use xso::error::{Error, FromElementError};
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
@ -78,7 +78,7 @@ mod tests {
.unwrap(); .unwrap();
let error = StanzaId::try_from(elem).unwrap_err(); let error = StanzaId::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in stanza-id element."); assert_eq!(message, "Unknown child in stanza-id element.");
@ -89,7 +89,7 @@ mod tests {
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0'/>".parse().unwrap(); let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0'/>".parse().unwrap();
let error = StanzaId::try_from(elem).unwrap_err(); let error = StanzaId::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'id' missing."); assert_eq!(message, "Required attribute 'id' missing.");
@ -102,7 +102,7 @@ mod tests {
.unwrap(); .unwrap();
let error = StanzaId::try_from(elem).unwrap_err(); let error = StanzaId::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'by' missing."); assert_eq!(message, "Required attribute 'by' missing.");

View file

@ -7,10 +7,10 @@
use crate::date::DateTime; use crate::date::DateTime;
use crate::iq::{IqGetPayload, IqResultPayload}; use crate::iq::{IqGetPayload, IqResultPayload};
use crate::ns; use crate::ns;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use chrono::FixedOffset; use chrono::FixedOffset;
use std::str::FromStr; use std::str::FromStr;
use xso::error::{Error, FromElementError};
generate_empty_element!( generate_empty_element!(
/// An entity time query. /// An entity time query.
@ -28,9 +28,9 @@ pub struct TimeResult(pub DateTime);
impl IqResultPayload for TimeResult {} impl IqResultPayload for TimeResult {}
impl TryFrom<Element> for TimeResult { impl TryFrom<Element> for TimeResult {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<TimeResult, Error> { fn try_from(elem: Element) -> Result<TimeResult, FromElementError> {
check_self!(elem, "time", TIME); check_self!(elem, "time", TIME);
check_no_attributes!(elem, "time"); check_no_attributes!(elem, "time");
@ -40,33 +40,34 @@ impl TryFrom<Element> for TimeResult {
for child in elem.children() { for child in elem.children() {
if child.is("tzo", ns::TIME) { if child.is("tzo", ns::TIME) {
if tzo.is_some() { if tzo.is_some() {
return Err(Error::ParseError("More than one tzo element in time.")); return Err(Error::Other("More than one tzo element in time.").into());
} }
check_no_children!(child, "tzo"); check_no_children!(child, "tzo");
check_no_attributes!(child, "tzo"); check_no_attributes!(child, "tzo");
// TODO: Add a FromStr implementation to FixedOffset to avoid this hack. // TODO: Add a FromStr implementation to FixedOffset to avoid this hack.
let fake_date = format!("{}{}", "2019-04-22T11:38:00", child.text()); let fake_date = format!("{}{}", "2019-04-22T11:38:00", child.text());
let date_time = DateTime::from_str(&fake_date)?; let date_time = DateTime::from_str(&fake_date).map_err(Error::text_parse_error)?;
tzo = Some(date_time.timezone()); tzo = Some(date_time.timezone());
} else if child.is("utc", ns::TIME) { } else if child.is("utc", ns::TIME) {
if utc.is_some() { if utc.is_some() {
return Err(Error::ParseError("More than one utc element in time.")); return Err(Error::Other("More than one utc element in time.").into());
} }
check_no_children!(child, "utc"); check_no_children!(child, "utc");
check_no_attributes!(child, "utc"); check_no_attributes!(child, "utc");
let date_time = DateTime::from_str(&child.text())?; let date_time =
DateTime::from_str(&child.text()).map_err(Error::text_parse_error)?;
match FixedOffset::east_opt(0) { match FixedOffset::east_opt(0) {
Some(tz) if date_time.timezone() == tz => (), Some(tz) if date_time.timezone() == tz => (),
_ => return Err(Error::ParseError("Non-UTC timezone for utc element.")), _ => return Err(Error::Other("Non-UTC timezone for utc element.").into()),
} }
utc = Some(date_time); utc = Some(date_time);
} else { } else {
return Err(Error::ParseError("Unknown child in time element.")); return Err(Error::Other("Unknown child in time element.").into());
} }
} }
let tzo = tzo.ok_or(Error::ParseError("Missing tzo child in time element."))?; let tzo = tzo.ok_or(Error::Other("Missing tzo child in time element."))?;
let utc = utc.ok_or(Error::ParseError("Missing utc child in time element."))?; let utc = utc.ok_or(Error::Other("Missing utc child in time element."))?;
let date = utc.with_timezone(tzo); let date = utc.with_timezone(tzo);
Ok(TimeResult(date)) Ok(TimeResult(date))

View file

@ -6,8 +6,8 @@
use crate::ns; use crate::ns;
use crate::pubsub::PubSubPayload; use crate::pubsub::PubSubPayload;
use crate::util::error::Error;
use crate::Element; use crate::Element;
use xso::error::{Error, FromElementError};
generate_elem_id!( generate_elem_id!(
/// The artist or performer of the song or piece. /// The artist or performer of the song or piece.
@ -106,9 +106,9 @@ impl Tune {
} }
impl TryFrom<Element> for Tune { impl TryFrom<Element> for Tune {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<Tune, Error> { fn try_from(elem: Element) -> Result<Tune, FromElementError> {
check_self!(elem, "tune", TUNE); check_self!(elem, "tune", TUNE);
check_no_attributes!(elem, "tune"); check_no_attributes!(elem, "tune");
@ -116,41 +116,41 @@ impl TryFrom<Element> for Tune {
for child in elem.children() { for child in elem.children() {
if child.is("artist", ns::TUNE) { if child.is("artist", ns::TUNE) {
if tune.artist.is_some() { if tune.artist.is_some() {
return Err(Error::ParseError("Tune cant have more than one artist.")); return Err(Error::Other("Tune cant have more than one artist.").into());
} }
tune.artist = Some(Artist::try_from(child.clone())?); tune.artist = Some(Artist::try_from(child.clone())?);
} else if child.is("length", ns::TUNE) { } else if child.is("length", ns::TUNE) {
if tune.length.is_some() { if tune.length.is_some() {
return Err(Error::ParseError("Tune cant have more than one length.")); return Err(Error::Other("Tune cant have more than one length.").into());
} }
tune.length = Some(Length::try_from(child.clone())?); tune.length = Some(Length::try_from(child.clone())?);
} else if child.is("rating", ns::TUNE) { } else if child.is("rating", ns::TUNE) {
if tune.rating.is_some() { if tune.rating.is_some() {
return Err(Error::ParseError("Tune cant have more than one rating.")); return Err(Error::Other("Tune cant have more than one rating.").into());
} }
tune.rating = Some(Rating::try_from(child.clone())?); tune.rating = Some(Rating::try_from(child.clone())?);
} else if child.is("source", ns::TUNE) { } else if child.is("source", ns::TUNE) {
if tune.source.is_some() { if tune.source.is_some() {
return Err(Error::ParseError("Tune cant have more than one source.")); return Err(Error::Other("Tune cant have more than one source.").into());
} }
tune.source = Some(Source::try_from(child.clone())?); tune.source = Some(Source::try_from(child.clone())?);
} else if child.is("title", ns::TUNE) { } else if child.is("title", ns::TUNE) {
if tune.title.is_some() { if tune.title.is_some() {
return Err(Error::ParseError("Tune cant have more than one title.")); return Err(Error::Other("Tune cant have more than one title.").into());
} }
tune.title = Some(Title::try_from(child.clone())?); tune.title = Some(Title::try_from(child.clone())?);
} else if child.is("track", ns::TUNE) { } else if child.is("track", ns::TUNE) {
if tune.track.is_some() { if tune.track.is_some() {
return Err(Error::ParseError("Tune cant have more than one track.")); return Err(Error::Other("Tune cant have more than one track.").into());
} }
tune.track = Some(Track::try_from(child.clone())?); tune.track = Some(Track::try_from(child.clone())?);
} else if child.is("uri", ns::TUNE) { } else if child.is("uri", ns::TUNE) {
if tune.uri.is_some() { if tune.uri.is_some() {
return Err(Error::ParseError("Tune cant have more than one uri.")); return Err(Error::Other("Tune cant have more than one uri.").into());
} }
tune.uri = Some(Uri::try_from(child.clone())?); tune.uri = Some(Uri::try_from(child.clone())?);
} else { } else {
return Err(Error::ParseError("Unknown element in User Tune.")); return Err(Error::Other("Unknown element in User Tune.").into());
} }
} }

View file

@ -1,148 +0,0 @@
// Copyright (c) 2017-2018 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::error::Error as StdError;
use std::fmt;
/// Contains one of the potential errors triggered while parsing an
/// [Element](../struct.Element.html) into a specialised struct.
#[derive(Debug)]
pub enum Error {
/// The usual error when parsing something.
///
/// TODO: use a structured error so the user can report it better, instead
/// of a freeform string.
ParseError(&'static str),
/// Element local-name/namespace mismatch
///
/// Returns the original element unaltered, as well as the expected ns and
/// local-name.
TypeMismatch(&'static str, &'static str, crate::Element),
/// Generated when some base64 content fails to decode, usually due to
/// extra characters.
Base64Error(base64::DecodeError),
/// Generated when text which should be an integer fails to parse.
ParseIntError(std::num::ParseIntError),
/// Generated when text which should be a string fails to parse.
ParseStringError(std::string::ParseError),
/// Generated when text which should be an IP address (IPv4 or IPv6) fails
/// to parse.
ParseAddrError(std::net::AddrParseError),
/// Generated when text which should be a [JID](../../jid/struct.Jid.html)
/// fails to parse.
JidParseError(jid::Error),
/// Generated when text which should be a
/// [DateTime](../date/struct.DateTime.html) fails to parse.
ChronoParseError(chrono::ParseError),
}
impl Error {
/// Converts the TypeMismatch error to a generic ParseError
///
/// This must be used when TryFrom is called on children to avoid confusing
/// user code which assumes that TypeMismatch refers to the top level
/// element only.
pub(crate) fn hide_type_mismatch(self) -> Self {
match self {
Error::TypeMismatch(..) => Error::ParseError("Unexpected child element"),
other => other,
}
}
}
impl StdError for Error {
fn cause(&self) -> Option<&dyn StdError> {
match self {
Error::ParseError(_) | Error::TypeMismatch(..) => None,
Error::Base64Error(e) => Some(e),
Error::ParseIntError(e) => Some(e),
Error::ParseStringError(e) => Some(e),
Error::ParseAddrError(e) => Some(e),
Error::JidParseError(e) => Some(e),
Error::ChronoParseError(e) => Some(e),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::ParseError(s) => write!(fmt, "parse error: {}", s),
Error::TypeMismatch(ns, localname, element) => write!(
fmt,
"element type mismatch: expected {{{}}}{}, got {{{}}}{}",
ns,
localname,
element.ns(),
element.name()
),
Error::Base64Error(e) => write!(fmt, "base64 error: {}", e),
Error::ParseIntError(e) => write!(fmt, "integer parsing error: {}", e),
Error::ParseStringError(e) => write!(fmt, "string parsing error: {}", e),
Error::ParseAddrError(e) => write!(fmt, "IP address parsing error: {}", e),
Error::JidParseError(e) => write!(fmt, "JID parsing error: {}", e),
Error::ChronoParseError(e) => write!(fmt, "time parsing error: {}", e),
}
}
}
impl From<base64::DecodeError> for Error {
fn from(err: base64::DecodeError) -> Error {
Error::Base64Error(err)
}
}
impl From<std::num::ParseIntError> for Error {
fn from(err: std::num::ParseIntError) -> Error {
Error::ParseIntError(err)
}
}
impl From<std::string::ParseError> for Error {
fn from(err: std::string::ParseError) -> Error {
Error::ParseStringError(err)
}
}
impl From<std::net::AddrParseError> for Error {
fn from(err: std::net::AddrParseError) -> Error {
Error::ParseAddrError(err)
}
}
impl From<jid::Error> for Error {
fn from(err: jid::Error) -> Error {
Error::JidParseError(err)
}
}
impl From<chrono::ParseError> for Error {
fn from(err: chrono::ParseError) -> Error {
Error::ChronoParseError(err)
}
}
impl From<Error> for xso::error::Error {
fn from(other: Error) -> Self {
match other {
Error::ParseError(e) => Self::Other(e.to_string().into()),
Error::TypeMismatch { .. } => Self::TypeMismatch,
Error::Base64Error(e) => Self::TextParseError(Box::new(e)),
Error::ParseIntError(e) => Self::TextParseError(Box::new(e)),
Error::ParseStringError(e) => Self::TextParseError(Box::new(e)),
Error::ParseAddrError(e) => Self::TextParseError(Box::new(e)),
Error::JidParseError(e) => Self::TextParseError(Box::new(e)),
Error::ChronoParseError(e) => Self::TextParseError(Box::new(e)),
}
}
}

View file

@ -6,7 +6,13 @@
macro_rules! get_attr { macro_rules! get_attr {
($elem:ident, $attr:tt, $type:tt) => { ($elem:ident, $attr:tt, $type:tt) => {
get_attr!($elem, $attr, $type, value, value.parse()?) get_attr!(
$elem,
$attr,
$type,
value,
value.parse().map_err(xso::error::Error::text_parse_error)?
)
}; };
($elem:ident, $attr:tt, OptionEmpty, $value:ident, $func:expr) => { ($elem:ident, $attr:tt, OptionEmpty, $value:ident, $func:expr) => {
match $elem.attr($attr) { match $elem.attr($attr) {
@ -25,30 +31,27 @@ macro_rules! get_attr {
match $elem.attr($attr) { match $elem.attr($attr) {
Some($value) => $func, Some($value) => $func,
None => { None => {
return Err(crate::util::error::Error::ParseError(concat!( return Err(xso::error::Error::Other(
"Required attribute '", concat!("Required attribute '", $attr, "' missing.").into(),
$attr, )
"' missing." .into());
)));
} }
} }
}; };
($elem:ident, $attr:tt, RequiredNonEmpty, $value:ident, $func:expr) => { ($elem:ident, $attr:tt, RequiredNonEmpty, $value:ident, $func:expr) => {
match $elem.attr($attr) { match $elem.attr($attr) {
Some("") => { Some("") => {
return Err(crate::util::error::Error::ParseError(concat!( return Err(xso::error::Error::Other(
"Required attribute '", concat!("Required attribute '", $attr, "' must not be empty.").into(),
$attr, )
"' must not be empty." .into());
)));
} }
Some($value) => $func, Some($value) => $func,
None => { None => {
return Err(crate::util::error::Error::ParseError(concat!( return Err(xso::error::Error::Other(
"Required attribute '", concat!("Required attribute '", $attr, "' missing.").into(),
$attr, )
"' missing." .into());
)));
} }
} }
}; };
@ -71,11 +74,11 @@ macro_rules! generate_attribute {
),+ ),+
} }
impl ::std::str::FromStr for $elem { impl ::std::str::FromStr for $elem {
type Err = crate::util::error::Error; type Err = xso::error::Error;
fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { fn from_str(s: &str) -> Result<$elem, xso::error::Error> {
Ok(match s { Ok(match s {
$($b => $elem::$a),+, $($b => $elem::$a),+,
_ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), _ => return Err(xso::error::Error::Other(concat!("Unknown value for '", $name, "' attribute.")).into()),
}) })
} }
} }
@ -104,11 +107,11 @@ macro_rules! generate_attribute {
),+ ),+
} }
impl ::std::str::FromStr for $elem { impl ::std::str::FromStr for $elem {
type Err = crate::util::error::Error; type Err = xso::error::Error;
fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { fn from_str(s: &str) -> Result<$elem, xso::error::Error> {
Ok(match s { Ok(match s {
$($b => $elem::$a),+, $($b => $elem::$a),+,
_ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), _ => return Err(xso::error::Error::Other(concat!("Unknown value for '", $name, "' attribute.")).into()),
}) })
} }
} }
@ -137,11 +140,11 @@ macro_rules! generate_attribute {
None, None,
} }
impl ::std::str::FromStr for $elem { impl ::std::str::FromStr for $elem {
type Err = crate::util::error::Error; type Err = xso::error::Error;
fn from_str(s: &str) -> Result<Self, crate::util::error::Error> { fn from_str(s: &str) -> Result<Self, xso::error::Error> {
Ok(match s { Ok(match s {
$value => $elem::$symbol, $value => $elem::$symbol,
_ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), _ => return Err(xso::error::Error::Other(concat!("Unknown value for '", $name, "' attribute."))),
}) })
} }
} }
@ -169,12 +172,12 @@ macro_rules! generate_attribute {
False, False,
} }
impl ::std::str::FromStr for $elem { impl ::std::str::FromStr for $elem {
type Err = crate::util::error::Error; type Err = xso::error::Error;
fn from_str(s: &str) -> Result<Self, crate::util::error::Error> { fn from_str(s: &str) -> Result<Self, xso::error::Error> {
Ok(match s { Ok(match s {
"true" | "1" => $elem::True, "true" | "1" => $elem::True,
"false" | "0" => $elem::False, "false" | "0" => $elem::False,
_ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))), _ => return Err(xso::error::Error::Other(concat!("Unknown value for '", $name, "' attribute."))),
}) })
} }
} }
@ -197,9 +200,9 @@ macro_rules! generate_attribute {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct $elem(pub $type); pub struct $elem(pub $type);
impl ::std::str::FromStr for $elem { impl ::std::str::FromStr for $elem {
type Err = crate::util::error::Error; type Err = xso::error::Error;
fn from_str(s: &str) -> Result<Self, crate::util::error::Error> { fn from_str(s: &str) -> Result<Self, xso::error::Error> {
Ok($elem($type::from_str(s)?)) Ok($elem($type::from_str(s).map_err(xso::error::Error::text_parse_error)?))
} }
} }
impl ::minidom::IntoAttributeValue for $elem { impl ::minidom::IntoAttributeValue for $elem {
@ -229,14 +232,14 @@ macro_rules! generate_element_enum {
),+ ),+
} }
impl ::std::convert::TryFrom<crate::Element> for $elem { impl ::std::convert::TryFrom<crate::Element> for $elem {
type Error = crate::util::error::Error; type Error = xso::error::FromElementError;
fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { fn try_from(elem: crate::Element) -> Result<$elem, xso::error::FromElementError> {
check_ns_only!(elem, $name, $ns); check_ns_only!(elem, $name, $ns);
check_no_children!(elem, $name); check_no_children!(elem, $name);
check_no_attributes!(elem, $name); check_no_attributes!(elem, $name);
Ok(match elem.name() { Ok(match elem.name() {
$($enum_name => $elem::$enum,)+ $($enum_name => $elem::$enum,)+
_ => return Err(crate::util::error::Error::ParseError(concat!("This is not a ", $name, " element."))), _ => return Err(xso::error::Error::Other(concat!("This is not a ", $name, " element.")).into()),
}) })
} }
} }
@ -265,14 +268,14 @@ macro_rules! generate_attribute_enum {
),+ ),+
} }
impl ::std::convert::TryFrom<crate::Element> for $elem { impl ::std::convert::TryFrom<crate::Element> for $elem {
type Error = crate::util::error::Error; type Error = xso::error::FromElementError;
fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { fn try_from(elem: crate::Element) -> Result<$elem, xso::error::FromElementError> {
check_ns_only!(elem, $name, $ns); check_ns_only!(elem, $name, $ns);
check_no_children!(elem, $name); check_no_children!(elem, $name);
check_no_unknown_attributes!(elem, $name, [$attr]); check_no_unknown_attributes!(elem, $name, [$attr]);
Ok(match get_attr!(elem, $attr, Required) { Ok(match get_attr!(elem, $attr, Required) {
$($enum_name => $elem::$enum,)+ $($enum_name => $elem::$enum,)+
_ => return Err(crate::util::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))), _ => return Err(xso::error::Error::Other(concat!("Invalid ", $name, " ", $attr, " value.")).into()),
}) })
} }
} }
@ -294,11 +297,7 @@ macro_rules! check_self {
}; };
($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) { if !$elem.is($name, crate::ns::$ns) {
return Err(crate::util::error::Error::TypeMismatch( return Err(xso::error::FromElementError::Mismatch($elem));
$name,
crate::ns::$ns,
$elem,
));
} }
}; };
} }
@ -309,11 +308,10 @@ macro_rules! check_child {
}; };
($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) { if !$elem.is($name, crate::ns::$ns) {
return Err(crate::util::error::Error::ParseError(concat!( return Err(xso::error::Error::Other(
"This is not a ", concat!("This is not a ", $pretty_name, " element.").into(),
$pretty_name, )
" element." .into());
)));
} }
}; };
} }
@ -321,11 +319,10 @@ macro_rules! check_child {
macro_rules! check_ns_only { macro_rules! check_ns_only {
($elem:ident, $name:tt, $ns:ident) => { ($elem:ident, $name:tt, $ns:ident) => {
if !$elem.has_ns(crate::ns::$ns) { if !$elem.has_ns(crate::ns::$ns) {
return Err(crate::util::error::Error::ParseError(concat!( return Err(xso::error::Error::Other(
"This is not a ", concat!("This is not a ", $name, " element.").into(),
$name, )
" element." .into());
)));
} }
}; };
} }
@ -334,11 +331,10 @@ macro_rules! check_no_children {
($elem:ident, $name:tt) => { ($elem:ident, $name:tt) => {
#[cfg(not(feature = "disable-validation"))] #[cfg(not(feature = "disable-validation"))]
for _ in $elem.children() { for _ in $elem.children() {
return Err(crate::util::error::Error::ParseError(concat!( return Err(xso::error::Error::Other(
"Unknown child in ", concat!("Unknown child in ", $name, " element.").into(),
$name, )
" element." .into());
)));
} }
}; };
} }
@ -347,11 +343,10 @@ macro_rules! check_no_attributes {
($elem:ident, $name:tt) => { ($elem:ident, $name:tt) => {
#[cfg(not(feature = "disable-validation"))] #[cfg(not(feature = "disable-validation"))]
for _ in $elem.attrs() { for _ in $elem.attrs() {
return Err(crate::util::error::Error::ParseError(concat!( return Err(xso::error::Error::Other(
"Unknown attribute in ", concat!("Unknown attribute in ", $name, " element.").into(),
$name, )
" element." .into());
)));
} }
}; };
} }
@ -365,7 +360,7 @@ macro_rules! check_no_unknown_attributes {
continue; continue;
} }
)* )*
return Err(crate::util::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element."))); return Err(xso::error::Error::Other(concat!("Unknown attribute in ", $name, " element.")).into());
} }
); );
} }
@ -377,9 +372,9 @@ macro_rules! generate_empty_element {
pub struct $elem; pub struct $elem;
impl ::std::convert::TryFrom<crate::Element> for $elem { impl ::std::convert::TryFrom<crate::Element> for $elem {
type Error = crate::util::error::Error; type Error = xso::error::FromElementError;
fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { fn try_from(elem: crate::Element) -> Result<$elem, xso::error::FromElementError> {
check_self!(elem, $name, $ns); check_self!(elem, $name, $ns);
check_no_children!(elem, $name); check_no_children!(elem, $name);
check_no_attributes!(elem, $name); check_no_attributes!(elem, $name);
@ -402,8 +397,8 @@ macro_rules! generate_id {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct $elem(pub String); pub struct $elem(pub String);
impl ::std::str::FromStr for $elem { impl ::std::str::FromStr for $elem {
type Err = crate::util::error::Error; type Err = xso::error::Error;
fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { fn from_str(s: &str) -> Result<$elem, xso::error::Error> {
// TODO: add a way to parse that differently when needed. // TODO: add a way to parse that differently when needed.
Ok($elem(String::from(s))) Ok($elem(String::from(s)))
} }
@ -420,8 +415,8 @@ macro_rules! generate_elem_id {
($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => ( ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => (
generate_elem_id!($(#[$meta])* $elem, $name, $ns, String); generate_elem_id!($(#[$meta])* $elem, $name, $ns, String);
impl ::std::str::FromStr for $elem { impl ::std::str::FromStr for $elem {
type Err = crate::util::error::Error; type Err = xso::error::Error;
fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { fn from_str(s: &str) -> Result<$elem, xso::error::Error> {
// TODO: add a way to parse that differently when needed. // TODO: add a way to parse that differently when needed.
Ok($elem(String::from(s))) Ok($elem(String::from(s)))
} }
@ -432,13 +427,13 @@ macro_rules! generate_elem_id {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct $elem(pub $type); pub struct $elem(pub $type);
impl ::std::convert::TryFrom<crate::Element> for $elem { impl ::std::convert::TryFrom<crate::Element> for $elem {
type Error = crate::util::error::Error; type Error = xso::error::FromElementError;
fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { fn try_from(elem: crate::Element) -> Result<$elem, xso::error::FromElementError> {
check_self!(elem, $name, $ns); check_self!(elem, $name, $ns);
check_no_children!(elem, $name); check_no_children!(elem, $name);
check_no_attributes!(elem, $name); check_no_attributes!(elem, $name);
// TODO: add a way to parse that differently when needed. // TODO: add a way to parse that differently when needed.
Ok($elem(elem.text().parse()?)) Ok($elem(elem.text().parse().map_err(xso::error::Error::text_parse_error)?))
} }
} }
impl From<$elem> for crate::Element { impl From<$elem> for crate::Element {
@ -507,7 +502,7 @@ macro_rules! do_parse {
Ok($elem.text()) Ok($elem.text())
}; };
($elem:ident, $constructor:ident) => { ($elem:ident, $constructor:ident) => {
$constructor::try_from($elem) $constructor::try_from($elem).map_err(xso::error::Error::from)
}; };
} }
@ -520,13 +515,16 @@ macro_rules! do_parse_elem {
}; };
($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() { if $temp.is_some() {
Err(crate::util::error::Error::ParseError(concat!( Err(xso::error::Error::Other(
"Element ", concat!(
$parent_name, "Element ",
" must not have more than one ", $parent_name,
$name, " must not have more than one ",
" child." $name,
))) " child."
)
.into(),
))
} else { } else {
match do_parse!($elem, $constructor) { match do_parse!($elem, $constructor) {
Ok(v) => { Ok(v) => {
@ -539,13 +537,16 @@ macro_rules! do_parse_elem {
}; };
($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() { if $temp.is_some() {
Err(crate::util::error::Error::ParseError(concat!( Err(xso::error::Error::Other(
"Element ", concat!(
$parent_name, "Element ",
" must not have more than one ", $parent_name,
$name, " must not have more than one ",
" child." $name,
))) " child."
)
.into(),
))
} else { } else {
match do_parse!($elem, $constructor) { match do_parse!($elem, $constructor) {
Ok(v) => { Ok(v) => {
@ -558,13 +559,16 @@ macro_rules! do_parse_elem {
}; };
($temp:ident: Present = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => { ($temp:ident: Present = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
if $temp { if $temp {
Err(crate::util::error::Error::ParseError(concat!( Err(xso::error::Error::Other(
"Element ", concat!(
$parent_name, "Element ",
" must not have more than one ", $parent_name,
$name, " must not have more than one ",
" child." $name,
))) " child."
)
.into(),
))
} else { } else {
$temp = true; $temp = true;
Ok(()) Ok(())
@ -580,13 +584,9 @@ macro_rules! finish_parse_elem {
$temp $temp
}; };
($temp:ident: Required = $name:tt, $parent_name:tt) => { ($temp:ident: Required = $name:tt, $parent_name:tt) => {
$temp.ok_or(crate::util::error::Error::ParseError(concat!( $temp.ok_or(xso::error::Error::Other(
"Missing child ", concat!("Missing child ", $name, " in ", $parent_name, " element.").into(),
$name, ))?
" in ",
$parent_name,
" element."
)))?
}; };
($temp:ident: Present = $name:tt, $parent_name:tt) => { ($temp:ident: Present = $name:tt, $parent_name:tt) => {
$temp $temp
@ -694,9 +694,9 @@ macro_rules! generate_element {
} }
impl ::std::convert::TryFrom<crate::Element> for $elem { impl ::std::convert::TryFrom<crate::Element> for $elem {
type Error = crate::util::error::Error; type Error = xso::error::FromElementError;
fn try_from(mut elem: crate::Element) -> Result<$elem, crate::util::error::Error> { fn try_from(mut elem: crate::Element) -> Result<$elem, xso::error::FromElementError> {
check_self!(elem, $name, $ns); check_self!(elem, $name, $ns);
check_no_unknown_attributes!(elem, $name, [$($attr_name),*]); check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
$( $(
@ -726,14 +726,14 @@ macro_rules! generate_element {
let residual = if generate_child_test!(residual, $child_name, $child_ns) { let residual = if generate_child_test!(residual, $child_name, $child_ns) {
match do_parse_elem!($child_ident: $coucou = $child_constructor => residual, $child_name, $name) { match do_parse_elem!($child_ident: $coucou = $child_constructor => residual, $child_name, $name) {
Ok(()) => continue, Ok(()) => continue,
Err(other) => return Err(other), Err(other) => return Err(other.into()),
} }
} else { } else {
residual residual
}; };
)* )*
let _ = residual; let _ = residual;
return Err(crate::util::error::Error::ParseError(concat!("Unknown child in ", $name, " element."))); return Err(xso::error::Error::Other(concat!("Unknown child in ", $name, " element.")).into());
} }
Ok($elem { Ok($elem {
$( $(
@ -787,17 +787,15 @@ macro_rules! assert_size (
macro_rules! impl_pubsub_item { macro_rules! impl_pubsub_item {
($item:ident, $ns:ident) => { ($item:ident, $ns:ident) => {
impl ::std::convert::TryFrom<crate::Element> for $item { impl ::std::convert::TryFrom<crate::Element> for $item {
type Error = Error; type Error = FromElementError;
fn try_from(mut elem: crate::Element) -> Result<$item, Error> { fn try_from(mut elem: crate::Element) -> Result<$item, FromElementError> {
check_self!(elem, "item", $ns); check_self!(elem, "item", $ns);
check_no_unknown_attributes!(elem, "item", ["id", "publisher"]); check_no_unknown_attributes!(elem, "item", ["id", "publisher"]);
let mut payloads = elem.take_contents_as_children().collect::<Vec<_>>(); let mut payloads = elem.take_contents_as_children().collect::<Vec<_>>();
let payload = payloads.pop(); let payload = payloads.pop();
if !payloads.is_empty() { if !payloads.is_empty() {
return Err(Error::ParseError( return Err(Error::Other("More than a single payload in item element.").into());
"More than a single payload in item element.",
));
} }
Ok($item(crate::pubsub::Item { Ok($item(crate::pubsub::Item {
id: get_attr!(elem, "id", Option), id: get_attr!(elem, "id", Option),

View file

@ -4,9 +4,6 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
/// Error type returned by every parser on failure.
pub mod error;
/// Various helpers. /// Various helpers.
pub(crate) mod text_node_codecs; pub(crate) mod text_node_codecs;

View file

@ -4,10 +4,10 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::util::error::Error;
use base64::{engine::general_purpose::STANDARD as Base64Engine, Engine}; use base64::{engine::general_purpose::STANDARD as Base64Engine, Engine};
use jid::Jid; use jid::Jid;
use std::str::FromStr; use std::str::FromStr;
use xso::error::Error;
/// A trait for codecs that can decode and encode text nodes. /// A trait for codecs that can decode and encode text nodes.
pub trait Codec { pub trait Codec {
@ -70,7 +70,7 @@ where
match s.trim() { match s.trim() {
// TODO: This error message can be a bit opaque when used // TODO: This error message can be a bit opaque when used
// in-context; ideally it'd be configurable. // in-context; ideally it'd be configurable.
"" => Err(Error::ParseError( "" => Err(Error::Other(
"The text in the element's text node was empty after trimming.", "The text in the element's text node was empty after trimming.",
)), )),
trimmed => T::decode(trimmed), trimmed => T::decode(trimmed),
@ -89,7 +89,7 @@ impl Codec for Base64 {
type Decoded = Vec<u8>; type Decoded = Vec<u8>;
fn decode(s: &str) -> Result<Vec<u8>, Error> { fn decode(s: &str) -> Result<Vec<u8>, Error> {
Ok(Base64Engine.decode(s)?) Base64Engine.decode(s).map_err(Error::text_parse_error)
} }
fn encode(decoded: &Vec<u8>) -> Option<String> { fn encode(decoded: &Vec<u8>) -> Option<String> {
@ -109,7 +109,7 @@ impl Codec for WhitespaceAwareBase64 {
.filter(|ch| *ch != ' ' && *ch != '\n' && *ch != '\t') .filter(|ch| *ch != ' ' && *ch != '\n' && *ch != '\t')
.collect(); .collect();
Ok(Base64Engine.decode(s)?) Base64Engine.decode(s).map_err(Error::text_parse_error)
} }
fn encode(decoded: &Self::Decoded) -> Option<String> { fn encode(decoded: &Self::Decoded) -> Option<String> {
@ -125,12 +125,13 @@ impl<const N: usize> Codec for FixedHex<N> {
fn decode(s: &str) -> Result<Self::Decoded, Error> { fn decode(s: &str) -> Result<Self::Decoded, Error> {
if s.len() != 2 * N { if s.len() != 2 * N {
return Err(Error::ParseError("Invalid length")); return Err(Error::Other("Invalid length"));
} }
let mut bytes = [0u8; N]; let mut bytes = [0u8; N];
for i in 0..N { for i in 0..N {
bytes[i] = u8::from_str_radix(&s[2 * i..2 * i + 2], 16)?; bytes[i] =
u8::from_str_radix(&s[2 * i..2 * i + 2], 16).map_err(Error::text_parse_error)?;
} }
Ok(bytes) Ok(bytes)
@ -154,7 +155,8 @@ impl Codec for ColonSeparatedHex {
fn decode(s: &str) -> Result<Self::Decoded, Error> { fn decode(s: &str) -> Result<Self::Decoded, Error> {
let mut bytes = vec![]; let mut bytes = vec![];
for i in 0..(1 + s.len()) / 3 { for i in 0..(1 + s.len()) / 3 {
let byte = u8::from_str_radix(&s[3 * i..3 * i + 2], 16)?; let byte =
u8::from_str_radix(&s[3 * i..3 * i + 2], 16).map_err(Error::text_parse_error)?;
if 3 * i + 2 < s.len() { if 3 * i + 2 < s.len() {
assert_eq!(&s[3 * i + 2..3 * i + 3], ":"); assert_eq!(&s[3 * i + 2..3 * i + 3], ":");
} }
@ -179,7 +181,7 @@ impl Codec for JidCodec {
type Decoded = Jid; type Decoded = Jid;
fn decode(s: &str) -> Result<Jid, Error> { fn decode(s: &str) -> Result<Jid, Error> {
Ok(Jid::from_str(s)?) Jid::from_str(s).map_err(Error::text_parse_error)
} }
fn encode(jid: &Jid) -> Option<String> { fn encode(jid: &Jid) -> Option<String> {
@ -205,28 +207,28 @@ mod tests {
// What if we give it a string that's too long? // What if we give it a string that's too long?
let err = FixedHex::<3>::decode("01feEF01").unwrap_err(); let err = FixedHex::<3>::decode("01feEF01").unwrap_err();
assert_eq!(err.to_string(), "parse error: Invalid length"); assert_eq!(err.to_string(), "Invalid length");
// Too short? // Too short?
let err = FixedHex::<3>::decode("01fe").unwrap_err(); let err = FixedHex::<3>::decode("01fe").unwrap_err();
assert_eq!(err.to_string(), "parse error: Invalid length"); assert_eq!(err.to_string(), "Invalid length");
// Not-even numbers? // Not-even numbers?
let err = FixedHex::<3>::decode("01feE").unwrap_err(); let err = FixedHex::<3>::decode("01feE").unwrap_err();
assert_eq!(err.to_string(), "parse error: Invalid length"); assert_eq!(err.to_string(), "Invalid length");
// No colon supported. // No colon supported.
let err = FixedHex::<3>::decode("0:f:EF").unwrap_err(); let err = FixedHex::<3>::decode("0:f:EF").unwrap_err();
assert_eq!( assert_eq!(
err.to_string(), err.to_string(),
"integer parsing error: invalid digit found in string" "text parse error: invalid digit found in string"
); );
// No non-hex character allowed. // No non-hex character allowed.
let err = FixedHex::<3>::decode("01defg").unwrap_err(); let err = FixedHex::<3>::decode("01defg").unwrap_err();
assert_eq!( assert_eq!(
err.to_string(), err.to_string(),
"integer parsing error: invalid digit found in string" "text parse error: invalid digit found in string"
); );
} }
} }

View file

@ -55,14 +55,12 @@ pub struct VCard {
} }
impl TryFrom<Element> for VCard { impl TryFrom<Element> for VCard {
type Error = crate::util::error::Error; type Error = xso::error::Error;
fn try_from(value: Element) -> Result<Self, Self::Error> { fn try_from(value: Element) -> Result<Self, Self::Error> {
// Check that the root element is <vCard> // Check that the root element is <vCard>
if !value.is("vCard", ns::VCARD) { if !value.is("vCard", ns::VCARD) {
return Err(Error::ParseError( return Err(Error::Other("Root element is not <vCard xmlns='vcard-temp'>").into());
"Root element is not <vCard xmlns='vcard-temp'>",
));
} }
// Parse the <PHOTO> element, if any. // Parse the <PHOTO> element, if any.

View file

@ -6,9 +6,9 @@
use crate::message::MessagePayload; use crate::message::MessagePayload;
use crate::ns; use crate::ns;
use crate::util::error::Error;
use minidom::{Element, Node}; use minidom::{Element, Node};
use std::collections::HashMap; use std::collections::HashMap;
use xso::error::{Error, FromElementError};
// TODO: Use a proper lang type. // TODO: Use a proper lang type.
type Lang = String; type Lang = String;
@ -60,9 +60,9 @@ impl XhtmlIm {
impl MessagePayload for XhtmlIm {} impl MessagePayload for XhtmlIm {}
impl TryFrom<Element> for XhtmlIm { impl TryFrom<Element> for XhtmlIm {
type Error = Error; type Error = FromElementError;
fn try_from(elem: Element) -> Result<XhtmlIm, Error> { fn try_from(elem: Element) -> Result<XhtmlIm, FromElementError> {
check_self!(elem, "html", XHTML_IM); check_self!(elem, "html", XHTML_IM);
check_no_attributes!(elem, "html"); check_no_attributes!(elem, "html");
@ -75,13 +75,14 @@ impl TryFrom<Element> for XhtmlIm {
match bodies.insert(lang, body) { match bodies.insert(lang, body) {
None => (), None => (),
Some(_) => { Some(_) => {
return Err(Error::ParseError( return Err(Error::Other(
"Two identical language bodies found in XHTML-IM.", "Two identical language bodies found in XHTML-IM.",
)) )
.into())
} }
} }
} else { } else {
return Err(Error::ParseError("Unknown element in XHTML-IM.")); return Err(Error::Other("Unknown element in XHTML-IM.").into());
} }
} }
@ -544,7 +545,7 @@ mod tests {
.unwrap(); .unwrap();
let error = XhtmlIm::try_from(elem).unwrap_err(); let error = XhtmlIm::try_from(elem).unwrap_err();
let message = match error { let message = match error {
Error::ParseError(string) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Two identical language bodies found in XHTML-IM."); assert_eq!(message, "Two identical language bodies found in XHTML-IM.");

View file

@ -23,7 +23,7 @@ pub enum Error {
TextParseError(Box<dyn std::error::Error + Send + Sync + 'static>), TextParseError(Box<dyn std::error::Error + Send + Sync + 'static>),
/// Generic, unspecified other error. /// Generic, unspecified other error.
Other(Box<str>), Other(&'static str),
/// An element header did not match an expected element. /// An element header did not match an expected element.
/// ///
@ -33,6 +33,16 @@ pub enum Error {
TypeMismatch, TypeMismatch,
} }
impl Error {
/// Convenience function to create a [`Self::TextParseError`] variant.
///
/// This includes the `Box::new(.)` call, making it directly usable as
/// argument to [`Result::map_err`].
pub fn text_parse_error<T: std::error::Error + Send + Sync + 'static>(e: T) -> Self {
Self::TextParseError(Box::new(e))
}
}
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
@ -121,3 +131,75 @@ impl std::error::Error for FromEventsError {
} }
} }
} }
impl From<Error> for Result<minidom::Element, Error> {
fn from(other: Error) -> Self {
Self::Err(other)
}
}
/// Error returned by the `TryFrom<Element>` implementations.
#[derive(Debug)]
pub enum FromElementError {
/// The XML element header did not match the expectations of the type
/// implementing `TryFrom`.
///
/// Contains the original `Element` unmodified.
Mismatch(minidom::Element),
/// During processing of the element, an (unrecoverable) error occured.
Invalid(Error),
}
impl fmt::Display for FromElementError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Mismatch(ref el) => write!(
f,
"expected different XML element (got {} in namespace {})",
el.name(),
el.ns()
),
Self::Invalid(ref e) => fmt::Display::fmt(e, f),
}
}
}
impl std::error::Error for FromElementError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Mismatch(_) => None,
Self::Invalid(ref e) => Some(e),
}
}
}
impl From<Result<minidom::Element, Error>> for FromElementError {
fn from(other: Result<minidom::Element, Error>) -> Self {
match other {
Ok(v) => Self::Mismatch(v),
Err(e) => Self::Invalid(e),
}
}
}
impl From<Error> for FromElementError {
fn from(other: Error) -> Self {
Self::Invalid(other)
}
}
impl From<FromElementError> for Error {
fn from(other: FromElementError) -> Self {
match other {
FromElementError::Invalid(e) => e,
FromElementError::Mismatch(..) => Self::TypeMismatch,
}
}
}
impl From<core::convert::Infallible> for FromElementError {
fn from(other: core::convert::Infallible) -> Self {
match other {}
}
}

View file

@ -138,3 +138,38 @@ pub fn transform<T: FromXml, F: IntoXml>(from: F) -> Result<T, self::error::Erro
rxml::error::XmlError::InvalidEof("during transform"), rxml::error::XmlError::InvalidEof("during transform"),
)) ))
} }
/// Attempt to convert a [`minidom::Element`] into a type implementing
/// [`FromXml`], fallably.
///
/// Unlike [`transform`] (which can also be used with an element), this
/// function will return the element unharmed if its element header does not
/// match the expectations of `T`.
pub fn try_from_element<T: FromXml>(
from: minidom::Element,
) -> Result<T, self::error::FromElementError> {
let (qname, attrs) = minidom_compat::make_start_ev_parts(&from)?;
let mut sink = match T::from_events(qname, attrs) {
Ok(v) => v,
Err(self::error::FromEventsError::Mismatch { .. }) => {
return Err(self::error::FromElementError::Mismatch(from))
}
Err(self::error::FromEventsError::Invalid(e)) => {
return Err(self::error::FromElementError::Invalid(e))
}
};
let mut iter = from.into_event_iter()?;
iter.next().expect("first event from minidom::Element")?;
for event in iter {
let event = event?;
match sink.feed(event)? {
Some(v) => return Ok(v),
None => (),
}
}
// unreachable! instead of error here, because minidom::Element always
// produces the complete event sequence of a single element, and FromXml
// implementations must be constructible from that.
unreachable!("minidom::Element did not produce enough events to complete element")
}

View file

@ -55,7 +55,7 @@ enum IntoEventsInner {
// NOTE to developers: The limitations are not fully trivial to overcome: // NOTE to developers: The limitations are not fully trivial to overcome:
// the attributes use a BTreeMap internally, which does not offer a `drain` // the attributes use a BTreeMap internally, which does not offer a `drain`
// iterator. // iterator.
fn make_start_ev_parts(el: &Element) -> Result<(rxml::QName, AttrMap), Error> { pub fn make_start_ev_parts(el: &Element) -> Result<(rxml::QName, AttrMap), Error> {
let name = NcName::try_from(el.name())?; let name = NcName::try_from(el.name())?;
let namespace = Namespace::from(el.ns()); let namespace = Namespace::from(el.ns());