diff --git a/parsers/src/attention.rs b/parsers/src/attention.rs index 8e7577a578259fb0ebe0f04c64a0d449a443c1da..df59e14da65e5ce6dbf1890c5cb3df0d37b06ca1 100644 --- a/parsers/src/attention.rs +++ b/parsers/src/attention.rs @@ -18,9 +18,9 @@ impl MessagePayload for Attention {} #[cfg(test)] mod tests { use super::*; - #[cfg(not(feature = "disable-validation"))] - use crate::util::error::Error; use crate::Element; + #[cfg(not(feature = "disable-validation"))] + use xso::error::{Error, FromElementError}; #[test] fn test_size() { @@ -41,7 +41,7 @@ mod tests { .unwrap(); let error = Attention::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in attention element."); @@ -55,7 +55,7 @@ mod tests { .unwrap(); let error = Attention::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in attention element."); diff --git a/parsers/src/avatar.rs b/parsers/src/avatar.rs index 65ef22f7f6ad6bb3bf43c173ffa7c955a3f7f51a..6c10dec2708b26bd9799ed0fa838121b8b19e0a8 100644 --- a/parsers/src/avatar.rs +++ b/parsers/src/avatar.rs @@ -58,9 +58,9 @@ impl PubSubPayload for Data {} mod tests { use super::*; use crate::hashes::Algo; - #[cfg(not(feature = "disable-validation"))] - use crate::util::error::Error; use crate::Element; + #[cfg(not(feature = "disable-validation"))] + use xso::error::{Error, FromElementError}; #[cfg(target_pointer_width = "32")] #[test] @@ -119,7 +119,7 @@ mod tests { .unwrap(); let error = Data::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in data element.") diff --git a/parsers/src/bind.rs b/parsers/src/bind.rs index bc0184c108d59ab164b493b814ec3303ff0f38ec..5d4d0aaeceb5fd1b52ebb10993659e65e3a9f892 100644 --- a/parsers/src/bind.rs +++ b/parsers/src/bind.rs @@ -6,10 +6,10 @@ use crate::iq::{IqResultPayload, IqSetPayload}; use crate::ns; -use crate::util::error::Error; use crate::Element; use jid::{FullJid, Jid}; use std::str::FromStr; +use xso::error::{Error, FromElementError}; /// 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. @@ -34,23 +34,23 @@ impl BindQuery { impl IqSetPayload for BindQuery {} impl TryFrom for BindQuery { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "bind", BIND); check_no_attributes!(elem, "bind"); let mut resource = None; for child in elem.children() { 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) { check_no_attributes!(child, "resource"); check_no_children!(child, "resource"); resource = Some(child.text()); } 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 for Jid { } impl TryFrom for BindResponse { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "bind", BIND); check_no_attributes!(elem, "bind"); let mut jid = None; for child in elem.children() { 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) { check_no_attributes!(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 { - return Err(Error::ParseError("Unknown element in bind response.")); + return Err(Error::Other("Unknown element in bind response.").into()); } } Ok(BindResponse { jid: match jid { None => { - return Err(Error::ParseError( - "Bind response must contain a jid element.", - )) + return Err(Error::Other("Bind response must contain a jid element.").into()) } Some(jid) => jid, }, @@ -187,7 +185,7 @@ mod tests { .unwrap(); let error = BindQuery::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in resource element."); @@ -197,7 +195,7 @@ mod tests { .unwrap(); let error = BindQuery::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in resource element."); diff --git a/parsers/src/blocking.rs b/parsers/src/blocking.rs index 8acb588a8d6c9148f317d6c4380dec80d0b7369b..ecf3846ee294b1aa7cb04b41419c780f98971878 100644 --- a/parsers/src/blocking.rs +++ b/parsers/src/blocking.rs @@ -6,9 +6,9 @@ use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; -use crate::util::error::Error; use crate::Element; use jid::Jid; +use xso::error::FromElementError; generate_empty_element!( /// The element requesting the blocklist, the result iq will contain a @@ -30,9 +30,9 @@ macro_rules! generate_blocking_element { } impl TryFrom 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_no_attributes!(elem, $name); let mut items = vec!(); @@ -96,6 +96,8 @@ generate_empty_element!( #[cfg(test)] mod tests { + use xso::error::Error; + use super::*; #[cfg(target_pointer_width = "32")] @@ -165,7 +167,7 @@ mod tests { let request_elem = elem.clone(); let error = BlocklistRequest::try_from(request_elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in blocklist element."); @@ -173,7 +175,7 @@ mod tests { let result_elem = elem.clone(); let error = BlocklistResult::try_from(result_elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in blocklist element."); @@ -183,7 +185,7 @@ mod tests { .unwrap(); let error = Block::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in block element."); @@ -193,7 +195,7 @@ mod tests { .unwrap(); let error = Unblock::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in unblock element."); @@ -205,7 +207,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = BlocklistRequest::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in blocklist element."); diff --git a/parsers/src/bob.rs b/parsers/src/bob.rs index 9639bf50bbf28407158c75a1068025946c8d7d08..1138e75370252243c2831e7f7f6869031946dd60 100644 --- a/parsers/src/bob.rs +++ b/parsers/src/bob.rs @@ -5,10 +5,10 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::hashes::{Algo, Hash}; -use crate::util::error::Error; use crate::util::text_node_codecs::{Base64, Codec}; use minidom::IntoAttributeValue; use std::str::FromStr; +use xso::error::Error; /// A Content-ID, as defined in RFC2111. /// @@ -27,11 +27,11 @@ impl FromStr for ContentId { let temp: Vec<_> = match temp[..] { [lhs, rhs] => { 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() } - _ => return Err(Error::ParseError("Missing @ in cid URI.")), + _ => return Err(Error::Other("Missing @ in cid URI.")), }; let (algo, hex) = match temp[..] { [lhs, rhs] => { @@ -42,9 +42,9 @@ impl FromStr for ContentId { }; (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 }) } } @@ -89,6 +89,7 @@ generate_element!( mod tests { use super::*; use crate::Element; + use xso::error::FromElementError; #[cfg(target_pointer_width = "32")] #[test] @@ -135,14 +136,14 @@ mod tests { fn invalid_cid() { let error = "Hello world!".parse::().unwrap_err(); let message = match error { - Error::ParseError(string) => string, + Error::Other(string) => string, _ => panic!(), }; assert_eq!(message, "Missing @ in cid URI."); let error = "Hello world@bob.xmpp.org".parse::().unwrap_err(); let message = match error { - Error::ParseError(string) => string, + Error::Other(string) => string, _ => panic!(), }; assert_eq!(message, "Missing + in cid URI."); @@ -151,7 +152,7 @@ mod tests { .parse::() .unwrap_err(); let message = match error { - Error::ParseError(string) => string, + Error::Other(string) => string, _ => panic!(), }; assert_eq!(message, "Wrong domain for cid URI."); @@ -160,7 +161,7 @@ mod tests { .parse::() .unwrap_err(); let message = match error { - Error::ParseIntError(error) => error, + Error::TextParseError(error) if error.is::() => error, _ => panic!(), }; assert_eq!(message.to_string(), "invalid digit found in string"); @@ -173,7 +174,7 @@ mod tests { .unwrap(); let error = Data::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in data element."); diff --git a/parsers/src/bookmarks2.rs b/parsers/src/bookmarks2.rs index d3804778c86a60803075137ef0c86e976db5ac3a..8026cf57b6f2d8a2c340b2be6285890e7f8bae30 100644 --- a/parsers/src/bookmarks2.rs +++ b/parsers/src/bookmarks2.rs @@ -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. use crate::ns; -use crate::util::error::Error; use crate::Element; +use xso::error::{Error, FromElementError}; generate_attribute!( /// Whether a conference bookmark should be joined automatically. @@ -52,9 +52,9 @@ impl Conference { } impl TryFrom for Conference { - type Error = Error; + type Error = FromElementError; - fn try_from(root: Element) -> Result { + fn try_from(root: Element) -> Result { check_self!(root, "conference", BOOKMARKS2, "Conference"); check_no_unknown_attributes!(root, "Conference", ["autojoin", "name"]); @@ -69,33 +69,30 @@ impl TryFrom for Conference { for child in root.children() { if child.is("nick", ns::BOOKMARKS2) { if conference.nick.is_some() { - return Err(Error::ParseError( - "Conference must not have more than one nick.", - )); + return Err(Error::Other("Conference must not have more than one nick.").into()); } check_no_children!(child, "nick"); check_no_attributes!(child, "nick"); conference.nick = Some(child.text()); } else if child.is("password", ns::BOOKMARKS2) { if conference.password.is_some() { - return Err(Error::ParseError( - "Conference must not have more than one password.", - )); + return Err( + Error::Other("Conference must not have more than one password.").into(), + ); } check_no_children!(child, "password"); check_no_attributes!(child, "password"); conference.password = Some(child.text()); } else if child.is("extensions", ns::BOOKMARKS2) { if !conference.extensions.is_empty() { - return Err(Error::ParseError( + return Err(Error::Other( "Conference must not have more than one extensions element.", - )); + ) + .into()); } conference.extensions.extend(child.children().cloned()); } else { - return Err(Error::ParseError( - "Unknown element in bookmarks2 conference", - )); + return Err(Error::Other("Unknown element in bookmarks2 conference").into()); } } diff --git a/parsers/src/caps.rs b/parsers/src/caps.rs index 929f6efcff28176ffa769b0f9c607f07f2e40d77..77f7b30dde3ddc8d05afecfab6519aafcb6bc350 100644 --- a/parsers/src/caps.rs +++ b/parsers/src/caps.rs @@ -9,7 +9,6 @@ use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity}; use crate::hashes::{Algo, Hash}; use crate::ns; use crate::presence::PresencePayload; -use crate::util::error::Error; use crate::Element; use base64::{engine::general_purpose::STANDARD as Base64, Engine}; use blake2::Blake2bVar; @@ -17,6 +16,7 @@ use digest::{Digest, Update, VariableOutput}; use sha1::Sha1; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; +use xso::error::{Error, FromElementError}; /// Represents a capability hash for a given client. #[derive(Debug, Clone)] @@ -39,16 +39,16 @@ pub struct Caps { impl PresencePayload for Caps {} impl TryFrom for Caps { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "c", CAPS, "caps"); check_no_children!(elem, "caps"); check_no_unknown_attributes!(elem, "caps", ["hash", "ver", "ext", "node"]); let ver: String = get_attr!(elem, "ver", Required); let hash = Hash { algo: get_attr!(elem, "hash", Required), - hash: Base64.decode(ver)?, + hash: Base64.decode(ver).map_err(Error::text_parse_error)?, }; Ok(Caps { ext: get_attr!(elem, "ext", Option), @@ -253,7 +253,7 @@ mod tests { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=".parse().unwrap(); let error = Caps::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in caps element."); diff --git a/parsers/src/chatstates.rs b/parsers/src/chatstates.rs index e9c83a03870ccc646402849356e6f0ff993ab79d..4f7ff7fc8f2ec9b144abbb39dbc76137b5fb5130 100644 --- a/parsers/src/chatstates.rs +++ b/parsers/src/chatstates.rs @@ -33,8 +33,8 @@ impl MessagePayload for ChatState {} mod tests { use super::*; use crate::ns; - use crate::util::error::Error; use crate::Element; + use xso::error::{Error, FromElementError}; #[test] fn test_size() { @@ -56,7 +56,7 @@ mod tests { .unwrap(); let error = ChatState::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "This is not a chatstate element."); @@ -70,7 +70,7 @@ mod tests { .unwrap(); let error = ChatState::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in chatstate element."); @@ -84,7 +84,7 @@ mod tests { .unwrap(); let error = ChatState::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in chatstate element."); diff --git a/parsers/src/data_forms.rs b/parsers/src/data_forms.rs index b4a97fad03912d2e23f9a06f9247d30714774dcc..24a631de8c6b1617b335f95e38431d595b5e48c1 100644 --- a/parsers/src/data_forms.rs +++ b/parsers/src/data_forms.rs @@ -6,8 +6,8 @@ use crate::media_element::MediaElement; use crate::ns; -use crate::util::error::Error; use crate::Element; +use xso::error::{Error, FromElementError}; generate_element!( /// Represents one of the possible values for a list- field. @@ -168,9 +168,9 @@ impl Field { } impl TryFrom for Field { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "field", DATA_FORMS); check_no_unknown_attributes!(elem, "field", ["label", "type", "var"]); let mut field = Field { @@ -185,7 +185,7 @@ impl TryFrom for Field { }; 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() { @@ -195,14 +195,14 @@ impl TryFrom for Field { field.values.push(element.text()); } else if element.is("required", ns::DATA_FORMS) { 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_attributes!(element, "required"); field.required = true; } else if element.is("option", ns::DATA_FORMS) { 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())?; field.options.push(option); @@ -214,9 +214,9 @@ impl TryFrom for Field { check_no_attributes!(element, "desc"); field.desc = Some(element.text()); } else { - return Err(Error::ParseError( - "Field child isn’t a value, option or media element.", - )); + return Err( + Error::Other("Field child isn’t a value, option or media element.").into(), + ); } } Ok(field) @@ -299,9 +299,9 @@ impl DataForm { } impl TryFrom for DataForm { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "x", DATA_FORMS); check_no_unknown_attributes!(elem, "x", ["type"]); let type_ = get_attr!(elem, "type", Required); @@ -315,16 +315,14 @@ impl TryFrom for DataForm { for child in elem.children() { if child.is("title", ns::DATA_FORMS) { 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_attributes!(child, "title"); form.title = Some(child.text()); } else if child.is("instructions", ns::DATA_FORMS) { if form.instructions.is_some() { - return Err(Error::ParseError( - "More than one instructions in form element.", - )); + return Err(Error::Other("More than one instructions in form element.").into()); } check_no_children!(child, "instructions"); check_no_attributes!(child, "instructions"); @@ -334,17 +332,17 @@ impl TryFrom for DataForm { if field.is_form_type(&form.type_) { let mut field = field; 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 { - 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(); } else { form.fields.push(field); } } else { - return Err(Error::ParseError("Unknown child in data form element.")); + return Err(Error::Other("Unknown child in data form element.").into()); } } Ok(form) @@ -415,7 +413,7 @@ mod tests { .unwrap(); let error = DataForm::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'var' missing."); @@ -474,7 +472,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = DataForm::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'type' missing."); @@ -482,10 +480,10 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = DataForm::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, - _ => panic!(), + FromElementError::Invalid(Error::TextParseError(string)) => string, + other => panic!("unexpected result: {:?}", other), }; - assert_eq!(message, "Unknown value for 'type' attribute."); + assert_eq!(message.to_string(), "Unknown value for 'type' attribute."); } #[test] @@ -495,7 +493,7 @@ mod tests { .unwrap(); let error = DataForm::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in data form element."); @@ -516,7 +514,7 @@ mod tests { .unwrap(); let error = Option_::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Missing child value in option element."); @@ -524,7 +522,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Option_::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!( diff --git a/parsers/src/date.rs b/parsers/src/date.rs index 8540c4b5a744495468e9b336224782360bb90e57..672ee2df0c05f1f627ad46131644202bc7c074f1 100644 --- a/parsers/src/date.rs +++ b/parsers/src/date.rs @@ -4,7 +4,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::util::error::Error; use chrono::{DateTime as ChronoDateTime, FixedOffset}; use minidom::{IntoAttributeValue, Node}; use std::str::FromStr; @@ -33,9 +32,9 @@ impl DateTime { } impl FromStr for DateTime { - type Err = Error; + type Err = chrono::ParseError; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { Ok(DateTime(ChronoDateTime::parse_from_rfc3339(s)?)) } } @@ -80,51 +79,27 @@ mod tests { fn test_invalid_date() { // There is no thirteenth month. let error = DateTime::from_str("2017-13-01T12:23:34Z").unwrap_err(); - let message = match error { - Error::ChronoParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message.to_string(), "input is out of range"); + assert_eq!(error.to_string(), "input is out of range"); // Timezone ≥24:00 aren’t allowed. let error = DateTime::from_str("2017-05-27T12:11:02+25:00").unwrap_err(); - let message = match error { - Error::ChronoParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message.to_string(), "input is out of range"); + assert_eq!(error.to_string(), "input is out of range"); // Timezone without the : separator aren’t allowed. let error = DateTime::from_str("2017-05-27T12:11:02+0100").unwrap_err(); - let message = match error { - Error::ChronoParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message.to_string(), "input contains invalid characters"); + assert_eq!(error.to_string(), "input contains invalid characters"); // No seconds, error message could be improved. let error = DateTime::from_str("2017-05-27T12:11+01:00").unwrap_err(); - let message = match error { - Error::ChronoParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message.to_string(), "input contains invalid characters"); + assert_eq!(error.to_string(), "input contains invalid characters"); // TODO: maybe we’ll want to support this one, as per XEP-0082 §4. let error = DateTime::from_str("20170527T12:11:02+01:00").unwrap_err(); - let message = match error { - Error::ChronoParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message.to_string(), "input contains invalid characters"); + assert_eq!(error.to_string(), "input contains invalid characters"); // No timezone. let error = DateTime::from_str("2017-05-27T12:11:02").unwrap_err(); - let message = match error { - Error::ChronoParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message.to_string(), "premature end of input"); + assert_eq!(error.to_string(), "premature end of input"); } #[test] diff --git a/parsers/src/delay.rs b/parsers/src/delay.rs index 3aeeaf367f1ca3afe071596f7282894cd1623fbc..53532c453a0dbfd23559b2f1fe452ca2e74248be 100644 --- a/parsers/src/delay.rs +++ b/parsers/src/delay.rs @@ -32,10 +32,10 @@ impl PresencePayload for Delay {} #[cfg(test)] mod tests { use super::*; - use crate::util::error::Error; use crate::Element; use jid::BareJid; use std::str::FromStr; + use xso::error::{Error, FromElementError}; #[cfg(target_pointer_width = "32")] #[test] @@ -71,7 +71,7 @@ mod tests { .unwrap(); let error = Delay::try_from(elem.clone()).unwrap_err(); let returned_elem = match error { - Error::TypeMismatch(_, _, elem) => elem, + FromElementError::Mismatch(elem) => elem, _ => panic!(), }; assert_eq!(elem, returned_elem); @@ -84,7 +84,7 @@ mod tests { .unwrap(); let error = Delay::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in delay element."); diff --git a/parsers/src/disco.rs b/parsers/src/disco.rs index d31af5a38484d7fcce0e4bd97fcecce329dc5ddc..4daf3e6b78346aef75c78d0edab5bc7133e50621 100644 --- a/parsers/src/disco.rs +++ b/parsers/src/disco.rs @@ -8,9 +8,9 @@ use crate::data_forms::{DataForm, DataFormType}; use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; use crate::rsm::{SetQuery, SetResult}; -use crate::util::error::Error; use crate::Element; use jid::Jid; +use xso::error::{Error, FromElementError}; generate_element!( /// Structure representing a `` element. @@ -115,9 +115,9 @@ pub struct DiscoInfoResult { impl IqResultPayload for DiscoInfoResult {} impl TryFrom for DiscoInfoResult { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "query", DISCO_INFO, "disco#info result"); check_no_unknown_attributes!(elem, "disco#info result", ["node"]); @@ -138,30 +138,30 @@ impl TryFrom for DiscoInfoResult { } else if child.is("x", ns::DATA_FORMS) { let data_form = DataForm::try_from(child.clone())?; if data_form.type_ != DataFormType::Result_ { - return Err(Error::ParseError( - "Data form must have a 'result' type in disco#info.", - )); + return Err( + Error::Other("Data form must have a 'result' type in disco#info.").into(), + ); } 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); } 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"))] { if result.identities.is_empty() { - return Err(Error::ParseError( - "There must be at least one identity in disco#info.", - )); + return Err( + Error::Other("There must be at least one identity in disco#info.").into(), + ); } if result.features.is_empty() { - return Err(Error::ParseError( - "There must be at least one feature in disco#info.", - )); + return Err( + Error::Other("There must be at least one feature in disco#info.").into(), + ); } } @@ -313,7 +313,7 @@ mod tests { .unwrap(); let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown element in disco#info."); @@ -327,7 +327,7 @@ mod tests { .unwrap(); let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'category' missing."); @@ -338,7 +338,7 @@ mod tests { .unwrap(); let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'category' must not be empty."); @@ -346,7 +346,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'type' missing."); @@ -354,7 +354,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'type' must not be empty."); @@ -368,7 +368,7 @@ mod tests { .unwrap(); let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'var' missing."); @@ -381,7 +381,7 @@ mod tests { .unwrap(); let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!( @@ -392,7 +392,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = DiscoInfoResult::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "There must be at least one feature in disco#info."); diff --git a/parsers/src/ecaps2.rs b/parsers/src/ecaps2.rs index f5993c224a654f2dc3d3241c11ffe00009360017..3e6b1960e568ae8afe8931d130728a601a3bacec 100644 --- a/parsers/src/ecaps2.rs +++ b/parsers/src/ecaps2.rs @@ -9,12 +9,12 @@ use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity}; use crate::hashes::{Algo, Hash}; use crate::ns; use crate::presence::PresencePayload; -use crate::util::error::Error; use base64::{engine::general_purpose::STANDARD as Base64, Engine}; use blake2::Blake2bVar; use digest::{Digest, Update, VariableOutput}; use sha2::{Sha256, Sha512}; use sha3::{Sha3_256, Sha3_512}; +use xso::error::Error; generate_element!( /// Represents a set of capability hashes, all of them must correspond to @@ -80,7 +80,7 @@ fn compute_identities(identities: &[Identity]) -> Vec { fn compute_extensions(extensions: &[DataForm]) -> Result, Error> { for extension in extensions { 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| { @@ -163,8 +163,8 @@ pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result { hasher.finalize_variable(&mut vec).unwrap(); vec } - Algo::Sha_1 => return Err(Error::ParseError("Disabled algorithm sha-1: unsafe.")), - Algo::Unknown(_algo) => return Err(Error::ParseError("Unknown algorithm in ecaps2.")), + Algo::Sha_1 => return Err(Error::Other("Disabled algorithm sha-1: unsafe.").into()), + Algo::Unknown(_algo) => return Err(Error::Other("Unknown algorithm in ecaps2.").into()), }, algo, }) @@ -187,6 +187,7 @@ pub fn query_ecaps2(hash: Hash) -> DiscoInfoQuery { mod tests { use super::*; use crate::Element; + use xso::error::FromElementError; #[cfg(target_pointer_width = "32")] #[test] @@ -226,7 +227,7 @@ mod tests { let elem: Element = "K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=".parse().unwrap(); let error = ECaps2::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in c element."); diff --git a/parsers/src/eme.rs b/parsers/src/eme.rs index 256b878da11a50ef2a71d7ba077dd94800a48675..f3eed8d73a8df1907776a97605b88e6c29e1ba98 100644 --- a/parsers/src/eme.rs +++ b/parsers/src/eme.rs @@ -24,8 +24,8 @@ impl MessagePayload for ExplicitMessageEncryption {} #[cfg(test)] mod tests { use super::*; - use crate::util::error::Error; use crate::Element; + use xso::error::{Error, FromElementError}; #[cfg(target_pointer_width = "32")] #[test] @@ -61,7 +61,7 @@ mod tests { .unwrap(); let error = ExplicitMessageEncryption::try_from(elem.clone()).unwrap_err(); let returned_elem = match error { - Error::TypeMismatch(_, _, elem) => elem, + FromElementError::Mismatch(elem) => elem, _ => panic!(), }; assert_eq!(elem, returned_elem); @@ -74,7 +74,7 @@ mod tests { .unwrap(); let error = ExplicitMessageEncryption::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in encryption element."); diff --git a/parsers/src/forwarding.rs b/parsers/src/forwarding.rs index 0bfa1c3a3a3ee136ac368a98c5818295080a6114..1b07caf6825127ba43f30afae60ba83a82ec261b 100644 --- a/parsers/src/forwarding.rs +++ b/parsers/src/forwarding.rs @@ -26,8 +26,8 @@ generate_element!( #[cfg(test)] mod tests { use super::*; - use crate::util::error::Error; use crate::Element; + use xso::error::{Error, FromElementError}; #[cfg(target_pointer_width = "32")] #[test] @@ -54,7 +54,7 @@ mod tests { .unwrap(); let error = Forwarded::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in forwarded element."); diff --git a/parsers/src/hashes.rs b/parsers/src/hashes.rs index 379f34cd8c4f7b5047d4f5bfd14919bead164310..7ead2af161d14260c612a44804d6cb93260d310a 100644 --- a/parsers/src/hashes.rs +++ b/parsers/src/hashes.rs @@ -4,13 +4,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::util::error::Error; use crate::util::text_node_codecs::{Base64, Codec}; use base64::{engine::general_purpose::STANDARD as Base64Engine, Engine}; use minidom::IntoAttributeValue; use std::num::ParseIntError; use std::ops::{Deref, DerefMut}; use std::str::FromStr; +use xso::error::Error; /// List of the algorithms we support, or Unknown. #[allow(non_camel_case_types)] @@ -60,7 +60,7 @@ impl FromStr for Algo { fn from_str(s: &str) -> Result { Ok(match s { - "" => return Err(Error::ParseError("'algo' argument can’t be empty.")), + "" => return Err(Error::Other("'algo' argument can’t be empty.")), "sha-1" => Algo::Sha_1, "sha-256" => Algo::Sha_256, @@ -118,7 +118,10 @@ impl Hash { /// Like [new](#method.new) but takes base64-encoded data before decoding /// it. pub fn from_base64(algo: Algo, hash: &str) -> Result { - 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. @@ -205,6 +208,7 @@ impl Deref for Sha1HexAttribute { mod tests { use super::*; use crate::Element; + use xso::error::FromElementError; #[cfg(target_pointer_width = "32")] #[test] @@ -255,7 +259,7 @@ mod tests { .unwrap(); let error = Hash::try_from(elem.clone()).unwrap_err(); let returned_elem = match error { - Error::TypeMismatch(_, _, elem) => elem, + FromElementError::Mismatch(elem) => elem, _ => panic!(), }; assert_eq!(elem, returned_elem); @@ -268,7 +272,7 @@ mod tests { .unwrap(); let error = Hash::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in hash element."); diff --git a/parsers/src/http_upload.rs b/parsers/src/http_upload.rs index 21479fe9a188f6ac1952a72d52fbb9b65af1d43e..839052345df055ca5ea2bced4f3573ae3a538707 100644 --- a/parsers/src/http_upload.rs +++ b/parsers/src/http_upload.rs @@ -6,8 +6,8 @@ use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; -use crate::util::error::Error; use crate::Element; +use xso::error::{Error, FromElementError}; generate_element!( /// Requesting a slot @@ -40,8 +40,8 @@ pub enum Header { } impl TryFrom for Header { - type Error = Error; - fn try_from(elem: Element) -> Result { + type Error = FromElementError; + fn try_from(elem: Element) -> Result { check_self!(elem, "header", HTTP_UPLOAD); check_no_children!(elem, "header"); check_no_unknown_attributes!(elem, "header", ["name"]); @@ -53,9 +53,10 @@ impl TryFrom for Header { "cookie" => Header::Cookie(text), "expires" => Header::Expires(text), _ => { - return Err(Error::ParseError( + return Err(Error::Other( "Header name must be either 'Authorization', 'Cookie', or 'Expires'.", - )) + ) + .into()) } }) } diff --git a/parsers/src/ibb.rs b/parsers/src/ibb.rs index 6ff88ea19860609bc0b47cbd6394dd411e6b41cb..d74aed3657d7ab2c1d594ba47360622e029075c1 100644 --- a/parsers/src/ibb.rs +++ b/parsers/src/ibb.rs @@ -72,8 +72,8 @@ impl IqSetPayload for Close {} #[cfg(test)] mod tests { use super::*; - use crate::util::error::Error; use crate::Element; + use xso::error::{Error, FromElementError}; #[cfg(target_pointer_width = "32")] #[test] @@ -131,7 +131,7 @@ mod tests { .unwrap(); let error = Open::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'block-size' missing."); @@ -141,7 +141,11 @@ mod tests { .unwrap(); let error = Open::try_from(elem).unwrap_err(); let message = match error { - Error::ParseIntError(error) => error, + FromElementError::Invalid(Error::TextParseError(error)) + if error.is::() => + { + error + } _ => panic!(), }; assert_eq!(message.to_string(), "invalid digit found in string"); @@ -151,7 +155,7 @@ mod tests { .unwrap(); let error = Open::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(error) => error, + FromElementError::Invalid(Error::Other(error)) => error, _ => panic!(), }; assert_eq!(message, "Required attribute 'sid' missing."); @@ -162,9 +166,9 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Open::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::TextParseError(string)) => string, _ => panic!(), }; - assert_eq!(message, "Unknown value for 'stanza' attribute."); + assert_eq!(message.to_string(), "Unknown value for 'stanza' attribute."); } } diff --git a/parsers/src/ibr.rs b/parsers/src/ibr.rs index 13b25b18b718e0080c1f75c7ef90421051d7db63..df73f7c06ef5279ad26d2842e3753fb8ec970799 100644 --- a/parsers/src/ibr.rs +++ b/parsers/src/ibr.rs @@ -7,9 +7,9 @@ use crate::data_forms::DataForm; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; -use crate::util::error::Error; use crate::Element; use std::collections::HashMap; +use xso::error::{Error, FromElementError}; /// Query for registering against a service. #[derive(Debug, Clone)] @@ -35,9 +35,9 @@ impl IqSetPayload for Query {} impl IqResultPayload for Query {} impl TryFrom for Query { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "query", REGISTER, "IBR query"); let mut query = Query { registered: false, @@ -76,12 +76,12 @@ impl TryFrom for Query { } else if name == "remove" { query.remove = true; } 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) { query.form = Some(DataForm::try_from(child.clone())?); } else { - return Err(Error::ParseError("Unknown child in ibr element.")); + return Err(Error::Other("Unknown child in ibr element.").into()); } } Ok(query) diff --git a/parsers/src/idle.rs b/parsers/src/idle.rs index c5ea025b685ac957ce94b54105e43ce5a478f603..c2158a019ccf04c4382ce6b2bdf6c6cf65597dd4 100644 --- a/parsers/src/idle.rs +++ b/parsers/src/idle.rs @@ -21,9 +21,9 @@ impl PresencePayload for Idle {} #[cfg(test)] mod tests { use super::*; - use crate::util::error::Error; use crate::Element; use std::str::FromStr; + use xso::error::{Error, FromElementError}; #[test] fn test_size() { @@ -45,7 +45,7 @@ mod tests { .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in idle element."); @@ -56,7 +56,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'since' missing."); @@ -70,8 +70,12 @@ mod tests { .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { - Error::ChronoParseError(string) => string, - _ => panic!(), + FromElementError::Invalid(Error::TextParseError(string)) + if string.is::() => + { + string + } + other => panic!("unexpected result: {:?}", other), }; assert_eq!(message.to_string(), "input is out of range"); @@ -81,7 +85,11 @@ mod tests { .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { - Error::ChronoParseError(string) => string, + FromElementError::Invalid(Error::TextParseError(string)) + if string.is::() => + { + string + } _ => panic!(), }; assert_eq!(message.to_string(), "input is out of range"); @@ -92,7 +100,11 @@ mod tests { .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { - Error::ChronoParseError(string) => string, + FromElementError::Invalid(Error::TextParseError(string)) + if string.is::() => + { + string + } _ => panic!(), }; assert_eq!(message.to_string(), "input contains invalid characters"); @@ -103,7 +115,11 @@ mod tests { .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { - Error::ChronoParseError(string) => string, + FromElementError::Invalid(Error::TextParseError(string)) + if string.is::() => + { + string + } _ => panic!(), }; assert_eq!(message.to_string(), "input contains invalid characters"); @@ -114,7 +130,11 @@ mod tests { .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { - Error::ChronoParseError(string) => string, + FromElementError::Invalid(Error::TextParseError(string)) + if string.is::() => + { + string + } _ => panic!(), }; assert_eq!(message.to_string(), "input contains invalid characters"); @@ -125,7 +145,11 @@ mod tests { .unwrap(); let error = Idle::try_from(elem).unwrap_err(); let message = match error { - Error::ChronoParseError(string) => string, + FromElementError::Invalid(Error::TextParseError(string)) + if string.is::() => + { + string + } _ => panic!(), }; assert_eq!(message.to_string(), "premature end of input"); diff --git a/parsers/src/iq.rs b/parsers/src/iq.rs index 4d5e726d20f95ce34ebdccd488458bc6efbe5ff2..54b1ba9cd13f64ab794c887ed74864037805c179 100644 --- a/parsers/src/iq.rs +++ b/parsers/src/iq.rs @@ -7,10 +7,10 @@ use crate::ns; use crate::stanza_error::StanzaError; -use crate::util::error::Error; use crate::Element; use jid::Jid; use minidom::IntoAttributeValue; +use xso::error::{Error, FromElementError}; /// Should be implemented on every known payload of an ``. pub trait IqGetPayload: TryFrom + Into {} @@ -139,9 +139,9 @@ impl Iq { } impl TryFrom for Iq { - type Error = Error; + type Error = FromElementError; - fn try_from(root: Element) -> Result { + fn try_from(root: Element) -> Result { check_self!(root, "iq", DEFAULT_NS); let from = get_attr!(root, "from", Option); let to = get_attr!(root, "to", Option); @@ -152,16 +152,16 @@ impl TryFrom for Iq { let mut error_payload = None; for elem in root.children() { 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 elem.is("error", ns::DEFAULT_NS) { 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())?); } 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 { payload = Some(elem.clone()); @@ -172,13 +172,13 @@ impl TryFrom for Iq { if let Some(payload) = payload { IqType::Get(payload) } 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" { if let Some(payload) = payload { IqType::Set(payload) } 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" { if let Some(payload) = payload { @@ -190,10 +190,10 @@ impl TryFrom for Iq { if let Some(payload) = error_payload { IqType::Error(payload) } 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 { - return Err(Error::ParseError("Unknown iq type.")); + return Err(Error::Other("Unknown iq type.").into()); }; Ok(Iq { @@ -251,7 +251,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Iq::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'id' missing."); @@ -264,7 +264,7 @@ mod tests { .unwrap(); let error = Iq::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'type' missing."); @@ -418,7 +418,7 @@ mod tests { .unwrap(); let error = Iq::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Wrong number of children in iq element."); diff --git a/parsers/src/jingle.rs b/parsers/src/jingle.rs index b560e74cc5605e9d1b973ecd367c1dd059082ac9..1de9f2e540d8be8ddcb1fe810bfc4fff5e58d5c9 100644 --- a/parsers/src/jingle.rs +++ b/parsers/src/jingle.rs @@ -11,12 +11,12 @@ use crate::jingle_ice_udp::Transport as IceUdpTransport; use crate::jingle_rtp::Description as RtpDescription; use crate::jingle_s5b::Transport as Socks5Transport; use crate::ns; -use crate::util::error::Error; use crate::Element; use jid::Jid; use std::collections::BTreeMap; use std::fmt; use std::str::FromStr; +use xso::error::{Error, FromElementError}; generate_attribute!( /// The action attribute. @@ -428,7 +428,7 @@ impl FromStr for Reason { "unsupported-applications" => Reason::UnsupportedApplications, "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 for ReasonElement { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "reason", JINGLE); check_no_attributes!(elem, "reason"); let mut reason = None; @@ -499,24 +499,22 @@ impl TryFrom for ReasonElement { check_no_unknown_attributes!(child, "text", ["xml:lang"]); let lang = get_attr!(elem, "xml:lang", Default); if texts.insert(lang, child.text()).is_some() { - return Err(Error::ParseError( - "Text element present twice for the same xml:lang.", - )); + return Err( + Error::Other("Text element present twice for the same xml:lang.").into(), + ); } } else if child.has_ns(ns::JINGLE) { if reason.is_some() { - return Err(Error::ParseError( - "Reason must not have more than one reason.", - )); + return Err(Error::Other("Reason must not have more than one reason.").into()); } check_no_children!(child, "reason"); check_no_attributes!(child, "reason"); reason = Some(child.name().parse()?); } 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 doesn’t contain a valid reason."))?; + let reason = reason.ok_or(Error::Other("Reason doesn’t contain a valid reason."))?; Ok(ReasonElement { reason, texts }) } } @@ -616,9 +614,9 @@ impl Jingle { } impl TryFrom for Jingle { - type Error = Error; + type Error = FromElementError; - fn try_from(root: Element) -> Result { + fn try_from(root: Element) -> Result { check_self!(root, "jingle", JINGLE, "Jingle"); check_no_unknown_attributes!(root, "Jingle", ["action", "initiator", "responder", "sid"]); @@ -639,17 +637,13 @@ impl TryFrom for Jingle { jingle.contents.push(content); } else if child.is("reason", ns::JINGLE) { if jingle.reason.is_some() { - return Err(Error::ParseError( - "Jingle must not have more than one reason.", - )); + return Err(Error::Other("Jingle must not have more than one reason.").into()); } let reason = ReasonElement::try_from(child)?; jingle.reason = Some(reason); } else if child.is("group", ns::JINGLE_GROUPING) { if jingle.group.is_some() { - return Err(Error::ParseError( - "Jingle must not have more than one grouping.", - )); + return Err(Error::Other("Jingle must not have more than one grouping.").into()); } let group = Group::try_from(child)?; jingle.group = Some(group); @@ -726,7 +720,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'action' missing."); @@ -736,7 +730,7 @@ mod tests { .unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'sid' missing."); @@ -746,10 +740,10 @@ mod tests { .unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::TextParseError(string)) => string, _ => panic!(), }; - assert_eq!(message, "Unknown value for 'action' attribute."); + assert_eq!(message.to_string(), "Unknown value for 'action' attribute."); } #[test] @@ -775,7 +769,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'creator' missing."); @@ -783,7 +777,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'name' missing."); @@ -791,26 +785,35 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, - _ => panic!(), + FromElementError::Invalid(Error::TextParseError(string)) => string, + 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 = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::TextParseError(string)) => string, _ => panic!(), }; - assert_eq!(message, "Unknown value for 'senders' attribute."); + assert_eq!( + message.to_string(), + "Unknown value for 'senders' attribute." + ); let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::TextParseError(string)) => string, _ => panic!(), }; - assert_eq!(message, "Unknown value for 'senders' attribute."); + assert_eq!( + message.to_string(), + "Unknown value for 'senders' attribute." + ); } #[test] @@ -833,7 +836,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Reason doesn’t contain a valid reason."); @@ -841,7 +844,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown reason."); @@ -849,7 +852,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Reason contains a foreign element."); @@ -857,7 +860,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Jingle must not have more than one reason."); @@ -865,7 +868,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Jingle::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Text element present twice for the same xml:lang."); diff --git a/parsers/src/jingle_dtls_srtp.rs b/parsers/src/jingle_dtls_srtp.rs index 1101b4eb28f8c92cf36d3065fe751b1f53006d81..7d28af754b16ac711eeb7341c911480bda88bedf 100644 --- a/parsers/src/jingle_dtls_srtp.rs +++ b/parsers/src/jingle_dtls_srtp.rs @@ -5,8 +5,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::hashes::{Algo, Hash}; -use crate::util::error::Error; use crate::util::text_node_codecs::{Codec, ColonSeparatedHex}; +use xso::error::Error; generate_attribute!( /// Indicates which of the end points should initiate the TCP connection establishment. @@ -64,7 +64,7 @@ impl Fingerprint { hash: &str, ) -> Result { 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)) } } diff --git a/parsers/src/jingle_ft.rs b/parsers/src/jingle_ft.rs index 396a49781414b420e94af0386e12fbfb40a800f7..dc3a582d133ff2ba1544f77ed3e36d262cfc4882 100644 --- a/parsers/src/jingle_ft.rs +++ b/parsers/src/jingle_ft.rs @@ -8,10 +8,10 @@ use crate::date::DateTime; use crate::hashes::Hash; use crate::jingle::{ContentId, Creator}; use crate::ns; -use crate::util::error::Error; use minidom::{Element, Node}; use std::collections::BTreeMap; use std::str::FromStr; +use xso::error::{Error, FromElementError}; generate_element!( /// 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 /// string. pub fn with_date_str(mut self, date: &str) -> Result { - self.date = Some(DateTime::from_str(date)?); + self.date = Some(DateTime::from_str(date).map_err(Error::text_parse_error)?); Ok(self) } @@ -127,9 +127,9 @@ impl File { } impl TryFrom for File { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "file", JINGLE_FT); check_no_attributes!(elem, "file"); @@ -146,43 +146,41 @@ impl TryFrom for File { for child in elem.children() { if child.is("date", ns::JINGLE_FT) { 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) { if file.media_type.is_some() { - return Err(Error::ParseError( - "File must not have more than one media-type.", - )); + return Err(Error::Other("File must not have more than one media-type.").into()); } file.media_type = Some(child.text()); } else if child.is("name", ns::JINGLE_FT) { 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()); } else if child.is("desc", ns::JINGLE_FT) { let lang = get_attr!(child, "xml:lang", Default); let desc = Desc(child.text()); if file.descs.insert(lang, desc).is_some() { - return Err(Error::ParseError( - "Desc element present twice for the same xml:lang.", - )); + return Err( + Error::Other("Desc element present twice for the same xml:lang.").into(), + ); } } else if child.is("size", ns::JINGLE_FT) { 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) { 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())?); } else if child.is("hash", ns::HASHES) { file.hashes.push(Hash::try_from(child.clone())?); } 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 for Description { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "description", JINGLE_FT, "JingleFT description"); check_no_attributes!(elem, "JingleFT description"); let mut file = None; for child in elem.children() { if file.is_some() { - return Err(Error::ParseError( + return Err(Error::Other( "JingleFT description element must have exactly one child.", - )); + ) + .into()); } file = Some(File::try_from(child.clone())?); } if file.is_none() { - return Err(Error::ParseError( - "JingleFT description element must have exactly one child.", - )); + return Err( + Error::Other("JingleFT description element must have exactly one child.").into(), + ); } Ok(Description { file: file.unwrap(), @@ -277,24 +276,30 @@ pub struct Checksum { } impl TryFrom for Checksum { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "checksum", JINGLE_FT); check_no_unknown_attributes!(elem, "checksum", ["name", "creator"]); let mut file = None; for child in elem.children() { if file.is_some() { - return Err(Error::ParseError( - "JingleFT checksum element must have exactly one child.", - )); + return Err( + Error::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() { - return Err(Error::ParseError( - "JingleFT checksum element must have exactly one child.", - )); + return Err( + Error::Other("JingleFT checksum element must have exactly one child.").into(), + ); } Ok(Checksum { name: get_attr!(elem, "name", Required), @@ -451,7 +456,7 @@ mod tests { .unwrap(); let error = Description::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Desc element present twice for the same xml:lang."); @@ -471,7 +476,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Received::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in received element."); @@ -482,7 +487,7 @@ mod tests { .unwrap(); let error = Received::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'name' missing."); @@ -490,10 +495,13 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Received::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::TextParseError(string)) => string, _ => panic!(), }; - assert_eq!(message, "Unknown value for 'creator' attribute."); + assert_eq!( + message.to_string(), + "Unknown value for 'creator' attribute." + ); } #[cfg(not(feature = "disable-validation"))] @@ -502,7 +510,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Received::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in received element."); @@ -540,7 +548,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Checksum::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, other => panic!("unexpected error: {:?}", other), }; assert_eq!(message, "Unexpected child element"); @@ -548,7 +556,7 @@ mod tests { let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); let error = Checksum::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'name' missing."); @@ -556,10 +564,13 @@ mod tests { let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); let error = Checksum::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::TextParseError(string)) => string, _ => panic!(), }; - assert_eq!(message, "Unknown value for 'creator' attribute."); + assert_eq!( + message.to_string(), + "Unknown value for 'creator' attribute." + ); } #[cfg(not(feature = "disable-validation"))] @@ -568,7 +579,7 @@ mod tests { let elem: Element = "w0mcJylzCn+AfvuGdqkty2+KP48=".parse().unwrap(); let error = Checksum::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in checksum element."); @@ -611,7 +622,7 @@ mod tests { .unwrap(); let error = Range::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in range element."); diff --git a/parsers/src/jingle_ibb.rs b/parsers/src/jingle_ibb.rs index e3680907ba0529c31f2d31ef5162bf23e5d272be..c660ca280b6f98e624b0cd523beed47c2e8b0174 100644 --- a/parsers/src/jingle_ibb.rs +++ b/parsers/src/jingle_ibb.rs @@ -24,8 +24,8 @@ attributes: [ #[cfg(test)] mod tests { use super::*; - use crate::util::error::Error; use crate::Element; + use xso::error::{Error, FromElementError}; #[cfg(target_pointer_width = "32")] #[test] @@ -58,7 +58,7 @@ mod tests { .unwrap(); let error = Transport::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'block-size' missing."); @@ -69,7 +69,11 @@ mod tests { .unwrap(); let error = Transport::try_from(elem).unwrap_err(); let message = match error { - Error::ParseIntError(error) => error, + FromElementError::Invalid(Error::TextParseError(error)) + if error.is::() => + { + error + } _ => panic!(), }; assert_eq!( @@ -82,7 +86,11 @@ mod tests { .unwrap(); let error = Transport::try_from(elem).unwrap_err(); let message = match error { - Error::ParseIntError(error) => error, + FromElementError::Invalid(Error::TextParseError(error)) + if error.is::() => + { + error + } _ => panic!(), }; assert_eq!(message.to_string(), "invalid digit found in string"); @@ -93,7 +101,7 @@ mod tests { .unwrap(); let error = Transport::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'sid' missing."); @@ -104,9 +112,9 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Transport::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::TextParseError(string)) => string, _ => panic!(), }; - assert_eq!(message, "Unknown value for 'stanza' attribute."); + assert_eq!(message.to_string(), "Unknown value for 'stanza' attribute."); } } diff --git a/parsers/src/jingle_message.rs b/parsers/src/jingle_message.rs index 2206773b797d8c729a266b54f93d81f8e31dbf2a..49afa0a4f27808dcc22d162b85659a4dda6eefdb 100644 --- a/parsers/src/jingle_message.rs +++ b/parsers/src/jingle_message.rs @@ -6,8 +6,8 @@ use crate::jingle::SessionId; use crate::ns; -use crate::util::error::Error; use crate::Element; +use xso::error::{Error, FromElementError}; /// Defines a protocol for broadcasting Jingle requests to all of the clients /// of a user. @@ -47,27 +47,27 @@ fn check_empty_and_get_sid(elem: Element) -> Result { } impl TryFrom for JingleMI { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { 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() { "propose" => { let mut description = None; for child in elem.children() { 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() { - 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()); } JingleMI::Propose { sid: get_sid(elem)?, - description: description.ok_or(Error::ParseError( + description: description.ok_or(Error::Other( "Propose element doesn’t contain a description.", ))?, } @@ -76,7 +76,7 @@ impl TryFrom for JingleMI { "accept" => JingleMI::Accept(check_empty_and_get_sid(elem)?), "proceed" => JingleMI::Proceed(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(); let error = JingleMI::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in propose element."); diff --git a/parsers/src/jingle_s5b.rs b/parsers/src/jingle_s5b.rs index 5c9cf3059c958fdf02d4617a1641166fcadd1f64..bf8187eaf5e513ecc1143b88c971dc701c167c25 100644 --- a/parsers/src/jingle_s5b.rs +++ b/parsers/src/jingle_s5b.rs @@ -5,10 +5,10 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::ns; -use crate::util::error::Error; use crate::Element; use jid::Jid; use std::net::IpAddr; +use xso::error::{Error, FromElementError}; generate_attribute!( /// The type of the connection being proposed by this candidate. @@ -171,9 +171,9 @@ impl Transport { } impl TryFrom for Transport { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "transport", JINGLE_S5B); check_no_unknown_attributes!(elem, "transport", ["sid", "dstaddr", "mode"]); let sid = get_attr!(elem, "sid", Required); @@ -186,47 +186,50 @@ impl TryFrom for Transport { let mut candidates = match payload { Some(TransportPayload::Candidates(candidates)) => candidates, - Some(_) => return Err(Error::ParseError( + Some(_) => return Err(Error::Other( "Non-candidate child already present in JingleS5B transport element.", - )), + ) + .into()), None => vec![], }; candidates.push(Candidate::try_from(child.clone())?); TransportPayload::Candidates(candidates) } else if child.is("activated", ns::JINGLE_S5B) { if payload.is_some() { - return Err(Error::ParseError( + return Err(Error::Other( "Non-activated child already present in JingleS5B transport element.", - )); + ) + .into()); } let cid = get_attr!(child, "cid", Required); TransportPayload::Activated(cid) } else if child.is("candidate-error", ns::JINGLE_S5B) { if payload.is_some() { - return Err(Error::ParseError( + return Err(Error::Other( "Non-candidate-error child already present in JingleS5B transport element.", - )); + ) + .into()); } TransportPayload::CandidateError } else if child.is("candidate-used", ns::JINGLE_S5B) { if payload.is_some() { - return Err(Error::ParseError( + return Err(Error::Other( "Non-candidate-used child already present in JingleS5B transport element.", - )); + ) + .into()); } let cid = get_attr!(child, "cid", Required); TransportPayload::CandidateUsed(cid) } else if child.is("proxy-error", ns::JINGLE_S5B) { if payload.is_some() { - return Err(Error::ParseError( + return Err(Error::Other( "Non-proxy-error child already present in JingleS5B transport element.", - )); + ) + .into()); } TransportPayload::ProxyError } else { - return Err(Error::ParseError( - "Unknown child in JingleS5B transport element.", - )); + return Err(Error::Other("Unknown child in JingleS5B transport element.").into()); }); } let payload = payload.unwrap_or(TransportPayload::None); diff --git a/parsers/src/lib.rs b/parsers/src/lib.rs index db2993abbacfff656cf1275388e0c73f312bb253..2f98701af01042614972f407ef4443137cba575f 100644 --- a/parsers/src/lib.rs +++ b/parsers/src/lib.rs @@ -23,7 +23,7 @@ #![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 pub use jid::{self, BareJid, Error as JidParseError, FullJid, Jid}; pub use minidom::Element; diff --git a/parsers/src/mam.rs b/parsers/src/mam.rs index d3fe3bdb86595d3da4744a7507c69dff94d7700e..decddcdc2211eca556e55fdda408d1018baf8414 100644 --- a/parsers/src/mam.rs +++ b/parsers/src/mam.rs @@ -11,9 +11,9 @@ use crate::message::MessagePayload; use crate::ns; use crate::pubsub::NodeName; use crate::rsm::{SetQuery, SetResult}; -use crate::util::error::Error; use crate::Element; use minidom::Node; +use xso::error::{Error, FromElementError}; generate_id!( /// An identifier matching a result message to the query requesting it. @@ -41,8 +41,8 @@ impl IqSetPayload for Query {} impl IqResultPayload for Query {} impl TryFrom for Query { - type Error = Error; - fn try_from(elem: Element) -> Result { + type Error = FromElementError; + fn try_from(elem: Element) -> Result { check_self!(elem, "query", MAM); check_no_unknown_attributes!(elem, "query", ["queryid", "node"]); @@ -52,32 +52,34 @@ impl TryFrom for Query { for child in elem.children() { if child.is("x", ns::DATA_FORMS) { if form.is_some() { - return Err(Error::ParseError( - "Element query must not have more than one x child.", - )); + return Err( + Error::Other("Element query must not have more than one x child.").into(), + ); } form = Some(DataForm::try_from(child.clone())?); continue; } if child.is("set", ns::RSM) { if set.is_some() { - return Err(Error::ParseError( + return Err(Error::Other( "Element query must not have more than one set child.", - )); + ) + .into()); } set = Some(SetQuery::try_from(child.clone())?); continue; } if child.is("flip-page", ns::MAM) { if flip_page.is_some() { - return Err(Error::ParseError( + return Err(Error::Other( "Element query must not have more than one flip-page child.", - )); + ) + .into()); } flip_page = Some(true); continue; } - return Err(Error::ParseError("Unknown child in query element.")); + return Err(Error::Other("Unknown child in query element.").into()); } Ok(Query { queryid: match elem.attr("queryid") { @@ -296,7 +298,7 @@ mod tests { .unwrap(); let error = Query::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in query element."); diff --git a/parsers/src/mam_prefs.rs b/parsers/src/mam_prefs.rs index 52a9902cbe15d2a181ef2fe98c367ae14a07266b..50831ed8ecb030ae9549a1b8b5137c65af345ee1 100644 --- a/parsers/src/mam_prefs.rs +++ b/parsers/src/mam_prefs.rs @@ -6,9 +6,9 @@ use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; -use crate::util::error::Error; use jid::Jid; use minidom::{Element, Node}; +use xso::error::{Error, FromElementError}; generate_attribute!( /// Notes the default archiving preference for the user. @@ -44,9 +44,9 @@ impl IqSetPayload for Prefs {} impl IqResultPayload for Prefs {} impl TryFrom for Prefs { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "prefs", MAM); check_no_unknown_attributes!(elem, "prefs", ["default"]); let mut always = vec![]; @@ -55,19 +55,19 @@ impl TryFrom for Prefs { if child.is("always", ns::MAM) { for jid_elem in child.children() { 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) { for jid_elem in child.children() { 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 { - 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); diff --git a/parsers/src/media_element.rs b/parsers/src/media_element.rs index 417e2bcde446c5754fdd1f9d4acc2c52ea20ee7d..50e88500cfea1d446c8e123a9050fe28ca203f01 100644 --- a/parsers/src/media_element.rs +++ b/parsers/src/media_element.rs @@ -46,8 +46,8 @@ generate_element!( mod tests { use super::*; use crate::data_forms::DataForm; - use crate::util::error::Error; use crate::Element; + use xso::error::{Error, FromElementError}; #[cfg(target_pointer_width = "32")] #[test] @@ -98,7 +98,11 @@ mod tests { .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let error = match error { - Error::ParseIntError(error) => error, + FromElementError::Invalid(Error::TextParseError(error)) + if error.is::() => + { + error + } _ => panic!(), }; assert_eq!(error.to_string(), "cannot parse integer from empty string"); @@ -108,7 +112,11 @@ mod tests { .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let error = match error { - Error::ParseIntError(error) => error, + FromElementError::Invalid(Error::TextParseError(error)) + if error.is::() => + { + error + } _ => panic!(), }; assert_eq!(error.to_string(), "invalid digit found in string"); @@ -118,7 +126,11 @@ mod tests { .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let error = match error { - Error::ParseIntError(error) => error, + FromElementError::Invalid(Error::TextParseError(error)) + if error.is::() => + { + error + } _ => panic!(), }; assert_eq!(error.to_string(), "cannot parse integer from empty string"); @@ -128,7 +140,11 @@ mod tests { .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let error = match error { - Error::ParseIntError(error) => error, + FromElementError::Invalid(Error::TextParseError(error)) + if error.is::() => + { + error + } _ => panic!(), }; assert_eq!(error.to_string(), "invalid digit found in string"); @@ -141,7 +157,7 @@ mod tests { .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in media element."); @@ -155,7 +171,7 @@ mod tests { .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'type' missing."); @@ -165,7 +181,7 @@ mod tests { .unwrap(); let error = MediaElement::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!( diff --git a/parsers/src/message.rs b/parsers/src/message.rs index 992936f50345e9455f2ef99aec5678d3fae8fcab..ac0ff3fb90f331519611fb8cef047b3da2b8a7e8 100644 --- a/parsers/src/message.rs +++ b/parsers/src/message.rs @@ -5,10 +5,10 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::ns; -use crate::util::error::Error; use crate::Element; use jid::Jid; use std::collections::BTreeMap; +use xso::error::{Error, FromElementError}; /// Should be implemented on every known payload of a ``. pub trait MessagePayload: TryFrom + Into {} @@ -213,7 +213,7 @@ impl Message { /// the message. /// /// Elements which do not match the given type are not removed. - pub fn extract_payload>( + pub fn extract_payload>( &mut self, ) -> Result, Error> { let mut buf = Vec::with_capacity(self.payloads.len()); @@ -225,10 +225,10 @@ impl Message { result = Ok(Some(v)); break; } - Err(Error::TypeMismatch(_, _, residual)) => { + Err(FromElementError::Mismatch(residual)) => { buf.push(residual); } - Err(other) => { + Err(FromElementError::Invalid(other)) => { result = Err(other); break; } @@ -241,9 +241,9 @@ impl Message { } impl TryFrom for Message { - type Error = Error; + type Error = FromElementError; - fn try_from(root: Element) -> Result { + fn try_from(root: Element) -> Result { check_self!(root, "message", DEFAULT_NS); let from = get_attr!(root, "from", Option); let to = get_attr!(root, "to", Option); @@ -259,22 +259,23 @@ impl TryFrom for Message { let lang = get_attr!(elem, "xml:lang", Default); let body = Body(elem.text()); if bodies.insert(lang, body).is_some() { - return Err(Error::ParseError( - "Body element present twice for the same xml:lang.", - )); + return Err( + Error::Other("Body element present twice for the same xml:lang.").into(), + ); } } else if elem.is("subject", ns::DEFAULT_NS) { check_no_children!(elem, "subject"); let lang = get_attr!(elem, "xml:lang", Default); let subject = Subject(elem.text()); if subjects.insert(lang, subject).is_some() { - return Err(Error::ParseError( + return Err(Error::Other( "Subject element present twice for the same xml:lang.", - )); + ) + .into()); } } else if elem.is("thread", ns::DEFAULT_NS) { 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"); thread = Some(Thread(elem.text())); diff --git a/parsers/src/message_correct.rs b/parsers/src/message_correct.rs index 2291ede06a2813d57bae494ac9558d5f644907ad..7db218b6dcdffedca2f5bded2d32a48586446f35 100644 --- a/parsers/src/message_correct.rs +++ b/parsers/src/message_correct.rs @@ -21,8 +21,8 @@ impl MessagePayload for Replace {} #[cfg(test)] mod tests { use super::*; - use crate::util::error::Error; use crate::Element; + use xso::error::{Error, FromElementError}; #[cfg(target_pointer_width = "32")] #[test] @@ -52,7 +52,7 @@ mod tests { .unwrap(); let error = Replace::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in replace element."); @@ -65,7 +65,7 @@ mod tests { .unwrap(); let error = Replace::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in replace element."); @@ -78,7 +78,7 @@ mod tests { .unwrap(); let error = Replace::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'id' missing."); diff --git a/parsers/src/muc/muc.rs b/parsers/src/muc/muc.rs index b9eb8e8b241363030abff213af0a6a982a0cde1a..494fedba9bf46e4c8274faf67c7da61234040766 100644 --- a/parsers/src/muc/muc.rs +++ b/parsers/src/muc/muc.rs @@ -94,9 +94,9 @@ impl Muc { #[cfg(test)] mod tests { use super::*; - use crate::util::error::Error; use crate::Element; use std::str::FromStr; + use xso::error::{Error, FromElementError}; #[test] fn test_muc_simple() { @@ -113,7 +113,7 @@ mod tests { .unwrap(); let error = Muc::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in x element."); @@ -140,7 +140,7 @@ mod tests { .unwrap(); let error = Muc::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in x element."); diff --git a/parsers/src/muc/user.rs b/parsers/src/muc/user.rs index a04de98e7572f3bbeda178f37f6677608a31d646..857b8dda38c9990b87249ab2fc742288ffc77a65 100644 --- a/parsers/src/muc/user.rs +++ b/parsers/src/muc/user.rs @@ -8,8 +8,8 @@ use crate::message::MessagePayload; use crate::ns; use crate::presence::PresencePayload; -use crate::util::error::Error; use crate::Element; +use xso::error::{Error, FromElementError}; use jid::FullJid; @@ -94,9 +94,9 @@ pub enum Actor { } impl TryFrom for Actor { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "actor", MUC_USER); check_no_unknown_attributes!(elem, "actor", ["jid", "nick"]); check_no_children!(elem, "actor"); @@ -104,9 +104,9 @@ impl TryFrom for Actor { let nick = get_attr!(elem, "nick", Option); match (jid, nick) { - (Some(_), Some(_)) | (None, None) => Err(Error::ParseError( - "Either 'jid' or 'nick' attribute is required.", - )), + (Some(_), Some(_)) | (None, None) => { + Err(Error::Other("Either 'jid' or 'nick' attribute is required.").into()) + } (Some(jid), _) => Ok(Actor::Jid(jid)), (_, Some(nick)) => Ok(Actor::Nick(nick)), } @@ -343,7 +343,7 @@ mod tests { .unwrap(); let error = MucUser::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in x element."); @@ -370,7 +370,7 @@ mod tests { .unwrap(); let error = MucUser::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in x element."); @@ -391,7 +391,7 @@ mod tests { .unwrap(); let error = Status::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'code' missing."); @@ -407,7 +407,7 @@ mod tests { .unwrap(); let error = Status::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in status element."); @@ -429,7 +429,7 @@ mod tests { .unwrap(); let error = Status::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Invalid status code value."); @@ -442,7 +442,11 @@ mod tests { .unwrap(); let error = Status::try_from(elem).unwrap_err(); let error = match error { - Error::ParseIntError(error) => error, + FromElementError::Invalid(Error::TextParseError(error)) + if error.is::() => + { + error + } _ => panic!(), }; assert_eq!(error.to_string(), "invalid digit found in string"); @@ -455,7 +459,7 @@ mod tests { .unwrap(); let error = Actor::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Either 'jid' or 'nick' attribute is required."); @@ -470,7 +474,7 @@ mod tests { .unwrap(); let error = Actor::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Either 'jid' or 'nick' attribute is required."); @@ -530,7 +534,7 @@ mod tests { .unwrap(); let continue_ = Continue::try_from(elem).unwrap_err(); let message = match continue_ { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in continue element.".to_owned()); @@ -557,7 +561,7 @@ mod tests { .unwrap(); let error = Reason::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in reason element.".to_owned()); @@ -573,7 +577,7 @@ mod tests { .unwrap(); let error = Reason::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in reason element.".to_owned()); @@ -588,7 +592,7 @@ mod tests { .unwrap(); let error = Item::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in item element.".to_owned()); @@ -612,7 +616,7 @@ mod tests { .unwrap(); let error = Item::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'role' missing.".to_owned()); @@ -640,7 +644,7 @@ mod tests { .unwrap(); let error = Item::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!( diff --git a/parsers/src/nick.rs b/parsers/src/nick.rs index 729488140f4d789433acc73cd149e674b5586aad..c6c8a5df4bc98ed5fd90a33ba49484c69dcda9d8 100644 --- a/parsers/src/nick.rs +++ b/parsers/src/nick.rs @@ -14,9 +14,9 @@ generate_elem_id!( #[cfg(test)] mod tests { use super::*; - #[cfg(not(feature = "disable-validation"))] - use crate::util::error::Error; use crate::Element; + #[cfg(not(feature = "disable-validation"))] + use xso::error::{Error, FromElementError}; #[cfg(target_pointer_width = "32")] #[test] @@ -56,7 +56,7 @@ mod tests { .unwrap(); let error = Nick::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in nick element."); @@ -70,7 +70,7 @@ mod tests { .unwrap(); let error = Nick::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in nick element."); diff --git a/parsers/src/occupant_id.rs b/parsers/src/occupant_id.rs index e21cfe8ccbabfb6cea3e6588c546aeeb5da862f1..07f72a14b03980e97d6c76b20d569b42e0428b03 100644 --- a/parsers/src/occupant_id.rs +++ b/parsers/src/occupant_id.rs @@ -26,8 +26,8 @@ impl PresencePayload for OccupantId {} #[cfg(test)] mod tests { use super::*; - use crate::util::error::Error; use crate::Element; + use xso::error::{Error, FromElementError}; #[cfg(target_pointer_width = "32")] #[test] @@ -57,7 +57,7 @@ mod tests { .unwrap(); let error = OccupantId::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in occupant-id element."); @@ -70,7 +70,7 @@ mod tests { .unwrap(); let error = OccupantId::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'id' missing."); diff --git a/parsers/src/oob.rs b/parsers/src/oob.rs index 5d5b7b2f3bf567ef1f825b73bd642b732f21ebf6..a13b011a4ff547b691bd9fbd4489e897113a21bb 100644 --- a/parsers/src/oob.rs +++ b/parsers/src/oob.rs @@ -22,8 +22,8 @@ impl MessagePayload for Oob {} #[cfg(test)] mod tests { use super::*; - use crate::util::error::Error; use crate::Element; + use xso::error::{Error, FromElementError}; #[cfg(target_pointer_width = "32")] #[test] @@ -59,7 +59,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Oob::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Missing child url in x element."); diff --git a/parsers/src/ping.rs b/parsers/src/ping.rs index 9af3a14253bfc8fe2c3866ba037c735aa9925b28..8881c19409637e18a54e712b526acaec16a64097 100644 --- a/parsers/src/ping.rs +++ b/parsers/src/ping.rs @@ -20,9 +20,9 @@ impl IqGetPayload for Ping {} #[cfg(test)] mod tests { use super::*; - #[cfg(not(feature = "disable-validation"))] - use crate::util::error::Error; use crate::Element; + #[cfg(not(feature = "disable-validation"))] + use xso::error::{Error, FromElementError}; #[test] fn test_size() { @@ -50,7 +50,7 @@ mod tests { .unwrap(); let error = Ping::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in ping element."); @@ -62,7 +62,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Ping::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in ping element."); diff --git a/parsers/src/presence.rs b/parsers/src/presence.rs index be2d6a40106f21ca760aa6a85e17172607771c4a..33ae22846cf0344dab214ae5d0cd4e2f10f6d207 100644 --- a/parsers/src/presence.rs +++ b/parsers/src/presence.rs @@ -6,11 +6,11 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::ns; -use crate::util::error::Error; use jid::Jid; use minidom::{Element, IntoAttributeValue}; use std::collections::BTreeMap; use std::str::FromStr; +use xso::error::{Error, FromElementError}; /// Should be implemented on every known payload of a ``. pub trait PresencePayload: TryFrom + Into {} @@ -42,7 +42,7 @@ impl FromStr for Show { "dnd" => Show::Dnd, "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, _ => { - return Err(Error::ParseError( - "Invalid 'type' attribute on presence element.", - )); + return Err(Error::Other("Invalid 'type' attribute on presence element.").into()); } }) } @@ -281,9 +279,9 @@ impl Presence { } impl TryFrom for Presence { - type Error = Error; + type Error = FromElementError; - fn try_from(root: Element) -> Result { + fn try_from(root: Element) -> Result { check_self!(root, "presence", DEFAULT_NS); let mut show = None; let mut priority = None; @@ -300,9 +298,7 @@ impl TryFrom for Presence { for elem in root.children() { if elem.is("show", ns::DEFAULT_NS) { if show.is_some() { - return Err(Error::ParseError( - "More than one show element in a presence.", - )); + return Err(Error::Other("More than one show element in a presence.").into()); } check_no_attributes!(elem, "show"); check_no_children!(elem, "show"); @@ -312,19 +308,22 @@ impl TryFrom for Presence { check_no_children!(elem, "status"); let lang = get_attr!(elem, "xml:lang", Default); if presence.statuses.insert(lang, elem.text()).is_some() { - return Err(Error::ParseError( + return Err(Error::Other( "Status element present twice for the same xml:lang.", - )); + ) + .into()); } } else if elem.is("priority", ns::DEFAULT_NS) { if priority.is_some() { - return Err(Error::ParseError( - "More than one priority element in a presence.", - )); + return Err( + Error::Other("More than one priority element in a presence.").into(), + ); } check_no_attributes!(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 { presence.payloads.push(elem.clone()); } @@ -461,7 +460,7 @@ mod tests { .unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Invalid value for show."); @@ -481,7 +480,7 @@ mod tests { .unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Invalid value for show."); @@ -541,7 +540,7 @@ mod tests { let elem: Element = "Here!Là!".parse().unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!( @@ -579,7 +578,11 @@ mod tests { .unwrap(); let error = Presence::try_from(elem).unwrap_err(); match error { - Error::ParseIntError(_) => (), + FromElementError::Invalid(Error::TextParseError(e)) + if e.is::() => + { + () + } _ => panic!(), }; } @@ -614,7 +617,7 @@ mod tests { .unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in status element."); @@ -634,7 +637,7 @@ mod tests { .unwrap(); let error = Presence::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in status element."); diff --git a/parsers/src/pubsub/event.rs b/parsers/src/pubsub/event.rs index 14612201b7492cd1339ab049281128826023e646..a2b0c8fcccee5ae7e94799759efd6a9c5071f973 100644 --- a/parsers/src/pubsub/event.rs +++ b/parsers/src/pubsub/event.rs @@ -9,9 +9,9 @@ use crate::date::DateTime; use crate::message::MessagePayload; use crate::ns; use crate::pubsub::{Item as PubSubItem, ItemId, NodeName, Subscription, SubscriptionId}; -use crate::util::error::Error; use crate::Element; use jid::Jid; +use xso::error::{Error, FromElementError}; /// Event wrapper for a PubSub ``. #[derive(Debug, Clone, PartialEq)] @@ -97,9 +97,7 @@ fn parse_items(elem: Element, node: NodeName) -> Result { None => is_retract = Some(false), Some(false) => (), Some(true) => { - return Err(Error::ParseError( - "Mix of item and retract in items element.", - )); + return Err(Error::Other("Mix of item and retract in items element.").into()); } } items.push(Item::try_from(child.clone())?); @@ -108,9 +106,7 @@ fn parse_items(elem: Element, node: NodeName) -> Result { None => is_retract = Some(true), Some(true) => (), Some(false) => { - return Err(Error::ParseError( - "Mix of item and retract in items element.", - )); + return Err(Error::Other("Mix of item and retract in items element.").into()); } } check_no_children!(child, "retract"); @@ -118,7 +114,7 @@ fn parse_items(elem: Element, node: NodeName) -> Result { let id = get_attr!(child, "id", Required); retracts.push(id); } else { - return Err(Error::ParseError("Invalid child in items element.")); + return Err(Error::Other("Invalid child in items element.").into()); } } Ok(match is_retract { @@ -127,14 +123,14 @@ fn parse_items(elem: Element, node: NodeName) -> Result { node, 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 for PubSubEvent { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "event", PUBSUB_EVENT); check_no_attributes!(elem, "event"); @@ -145,9 +141,10 @@ impl TryFrom for PubSubEvent { let mut payloads = child.children().cloned().collect::>(); let item = payloads.pop(); if !payloads.is_empty() { - return Err(Error::ParseError( + return Err(Error::Other( "More than a single payload in configuration element.", - )); + ) + .into()); } let form = match item { None => None, @@ -159,14 +156,14 @@ impl TryFrom for PubSubEvent { for item in child.children() { if item.is("redirect", ns::PUBSUB_EVENT) { if redirect.is_some() { - return Err(Error::ParseError( - "More than one redirect in delete element.", - )); + return Err( + Error::Other("More than one redirect in delete element.").into() + ); } let uri = get_attr!(item, "uri", Required); redirect = Some(uri); } 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 }); @@ -185,10 +182,10 @@ impl TryFrom for PubSubEvent { subscription: get_attr!(child, "subscription", Option), }); } 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(); let error = PubSubEvent::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Missing children in items element."); @@ -375,7 +372,7 @@ mod tests { .unwrap(); let error = PubSubEvent::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in event element."); @@ -389,7 +386,7 @@ mod tests { .unwrap(); let error = PubSubEvent::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in event element."); diff --git a/parsers/src/pubsub/owner.rs b/parsers/src/pubsub/owner.rs index 93cb8ef4a9477084eff94a6348376b534a9960cb..75295235ba0c4aaf2f1ecd0790abdb966f56712c 100644 --- a/parsers/src/pubsub/owner.rs +++ b/parsers/src/pubsub/owner.rs @@ -9,9 +9,9 @@ use crate::data_forms::DataForm; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; use crate::pubsub::{AffiliationAttribute, NodeName, Subscription}; -use crate::util::error::Error; use crate::Element; use jid::Jid; +use xso::error::{Error, FromElementError}; generate_element!( /// 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 TryFrom for PubSubOwner { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "pubsub", PUBSUB_OWNER); check_no_attributes!(elem, "pubsub"); @@ -153,17 +153,18 @@ impl TryFrom for PubSubOwner { for child in elem.children() { if child.is("configure", ns::PUBSUB_OWNER) { if payload.is_some() { - return Err(Error::ParseError( + return Err(Error::Other( "Payload is already defined in pubsub owner element.", - )); + ) + .into()); } let configure = Configure::try_from(child.clone())?; payload = Some(PubSubOwner::Configure(configure)); } 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()) } } diff --git a/parsers/src/pubsub/pubsub.rs b/parsers/src/pubsub/pubsub.rs index f087ef663cadb0aced38bd99a6cab77603891722..a295e7c304e45de77ce10c602256c2c12aae9ba8 100644 --- a/parsers/src/pubsub/pubsub.rs +++ b/parsers/src/pubsub/pubsub.rs @@ -10,9 +10,9 @@ use crate::ns; use crate::pubsub::{ AffiliationAttribute, Item as PubSubItem, NodeName, Subscription, SubscriptionId, }; -use crate::util::error::Error; use crate::Element; 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 // XEP-0030. @@ -181,24 +181,23 @@ pub struct SubscribeOptions { } impl TryFrom for SubscribeOptions { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "subscribe-options", PUBSUB); check_no_attributes!(elem, "subscribe-options"); let mut required = false; for child in elem.children() { if child.is("required", ns::PUBSUB) { if required { - return Err(Error::ParseError( + return Err(Error::Other( "More than one required element in subscribe-options.", - )); + ) + .into()); } required = true; } else { - return Err(Error::ParseError( - "Unknown child in subscribe-options element.", - )); + return Err(Error::Other("Unknown child in subscribe-options element.").into()); } } Ok(SubscribeOptions { required }) @@ -338,9 +337,9 @@ impl IqSetPayload for PubSub {} impl IqResultPayload for PubSub {} impl TryFrom for PubSub { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "pubsub", PUBSUB); check_no_attributes!(elem, "pubsub"); @@ -348,9 +347,9 @@ impl TryFrom for PubSub { for child in elem.children() { if child.is("create", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError( - "Payload is already defined in pubsub element.", - )); + return Err( + Error::Other("Payload is already defined in pubsub element.").into(), + ); } let create = Create::try_from(child.clone())?; payload = Some(PubSub::Create { @@ -359,9 +358,9 @@ impl TryFrom for PubSub { }); } else if child.is("subscribe", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError( - "Payload is already defined in pubsub element.", - )); + return Err( + Error::Other("Payload is already defined in pubsub element.").into(), + ); } let subscribe = Subscribe::try_from(child.clone())?; payload = Some(PubSub::Subscribe { @@ -371,9 +370,9 @@ impl TryFrom for PubSub { } else if child.is("options", ns::PUBSUB) { if let Some(PubSub::Subscribe { subscribe, options }) = payload { if options.is_some() { - return Err(Error::ParseError( - "Options is already defined in pubsub element.", - )); + return Err( + Error::Other("Options is already defined in pubsub element.").into(), + ); } let options = Some(Options::try_from(child.clone())?); payload = Some(PubSub::Subscribe { subscribe, options }); @@ -384,29 +383,30 @@ impl TryFrom for PubSub { options: Some(options), }); } else { - return Err(Error::ParseError( - "Payload is already defined in pubsub element.", - )); + return Err( + Error::Other("Payload is already defined in pubsub element.").into(), + ); } } else if child.is("configure", ns::PUBSUB) { if let Some(PubSub::Create { create, configure }) = payload { if configure.is_some() { - return Err(Error::ParseError( + return Err(Error::Other( "Configure is already defined in pubsub element.", - )); + ) + .into()); } let configure = Some(Configure::try_from(child.clone())?); payload = Some(PubSub::Create { create, configure }); } else { - return Err(Error::ParseError( - "Payload is already defined in pubsub element.", - )); + return Err( + Error::Other("Payload is already defined in pubsub element.").into(), + ); } } else if child.is("publish", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError( - "Payload is already defined in pubsub element.", - )); + return Err( + Error::Other("Payload is already defined in pubsub element.").into(), + ); } let publish = Publish::try_from(child.clone())?; payload = Some(PubSub::Publish { @@ -420,9 +420,10 @@ impl TryFrom for PubSub { }) = payload { if publish_options.is_some() { - return Err(Error::ParseError( + return Err(Error::Other( "Publish-options are already defined in pubsub element.", - )); + ) + .into()); } let publish_options = Some(PublishOptions::try_from(child.clone())?); payload = Some(PubSub::Publish { @@ -430,71 +431,71 @@ impl TryFrom for PubSub { publish_options, }); } else { - return Err(Error::ParseError( - "Payload is already defined in pubsub element.", - )); + return Err( + Error::Other("Payload is already defined in pubsub element.").into(), + ); } } else if child.is("affiliations", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError( - "Payload is already defined in pubsub element.", - )); + return Err( + Error::Other("Payload is already defined in pubsub element.").into(), + ); } let affiliations = Affiliations::try_from(child.clone())?; payload = Some(PubSub::Affiliations(affiliations)); } else if child.is("default", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError( - "Payload is already defined in pubsub element.", - )); + return Err( + Error::Other("Payload is already defined in pubsub element.").into(), + ); } let default = Default::try_from(child.clone())?; payload = Some(PubSub::Default(default)); } else if child.is("items", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError( - "Payload is already defined in pubsub element.", - )); + return Err( + Error::Other("Payload is already defined in pubsub element.").into(), + ); } let items = Items::try_from(child.clone())?; payload = Some(PubSub::Items(items)); } else if child.is("retract", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError( - "Payload is already defined in pubsub element.", - )); + return Err( + Error::Other("Payload is already defined in pubsub element.").into(), + ); } let retract = Retract::try_from(child.clone())?; payload = Some(PubSub::Retract(retract)); } else if child.is("subscription", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError( - "Payload is already defined in pubsub element.", - )); + return Err( + Error::Other("Payload is already defined in pubsub element.").into(), + ); } let subscription = SubscriptionElem::try_from(child.clone())?; payload = Some(PubSub::Subscription(subscription)); } else if child.is("subscriptions", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError( - "Payload is already defined in pubsub element.", - )); + return Err( + Error::Other("Payload is already defined in pubsub element.").into(), + ); } let subscriptions = Subscriptions::try_from(child.clone())?; payload = Some(PubSub::Subscriptions(subscriptions)); } else if child.is("unsubscribe", ns::PUBSUB) { if payload.is_some() { - return Err(Error::ParseError( - "Payload is already defined in pubsub element.", - )); + return Err( + Error::Other("Payload is already defined in pubsub element.").into(), + ); } let unsubscribe = Unsubscribe::try_from(child.clone())?; payload = Some(PubSub::Unsubscribe(unsubscribe)); } 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(); let error = PubSub::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "No payload in pubsub element."); diff --git a/parsers/src/receipts.rs b/parsers/src/receipts.rs index a96f30319a07ebb4af8a85f1e685ade648785272..28ce036610afc3fe137999efa91eb247c5def841 100644 --- a/parsers/src/receipts.rs +++ b/parsers/src/receipts.rs @@ -32,8 +32,8 @@ impl MessagePayload for Received {} mod tests { use super::*; use crate::ns; - use crate::util::error::Error; use crate::Element; + use xso::error::{Error, FromElementError}; #[cfg(target_pointer_width = "32")] #[test] @@ -65,7 +65,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = Received::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'id' missing."); diff --git a/parsers/src/roster.rs b/parsers/src/roster.rs index 9eca2e927b3fd1cbbd0abea8b23582b714661a1b..fe6e8b3bdbde9152dab0f7f4ec36a33a6f0582c7 100644 --- a/parsers/src/roster.rs +++ b/parsers/src/roster.rs @@ -91,9 +91,9 @@ impl IqResultPayload for Roster {} #[cfg(test)] mod tests { use super::*; - use crate::util::error::Error; use crate::Element; use std::str::FromStr; + use xso::error::{Error, FromElementError}; #[cfg(target_pointer_width = "32")] #[test] @@ -270,7 +270,7 @@ mod tests { .unwrap(); let error = Roster::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in query element."); @@ -280,7 +280,7 @@ mod tests { .unwrap(); let error = Roster::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown attribute in query element."); @@ -293,7 +293,7 @@ mod tests { .unwrap(); let error = Roster::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'jid' missing."); @@ -314,7 +314,7 @@ mod tests { .unwrap(); let error = Roster::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in item element."); diff --git a/parsers/src/rsm.rs b/parsers/src/rsm.rs index bb5adf3819735d1f87f583ac26a6467116badfce..7ffc6f2d3861abf83c2ba7ad51401bfd8c825554 100644 --- a/parsers/src/rsm.rs +++ b/parsers/src/rsm.rs @@ -5,8 +5,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::ns; -use crate::util::error::Error; use crate::Element; +use xso::error::{Error, FromElementError}; /// Requests paging through a potentially big set of items (represented by an /// UID). @@ -28,9 +28,9 @@ pub struct SetQuery { } impl TryFrom for SetQuery { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "set", RSM, "RSM set"); let mut set = SetQuery { max: None, @@ -41,26 +41,26 @@ impl TryFrom for SetQuery { for child in elem.children() { if child.is("max", ns::RSM) { if set.max.is_some() { - return Err(Error::ParseError("Set can’t have more than one max.")); + return Err(Error::Other("Set can’t 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) { if set.after.is_some() { - return Err(Error::ParseError("Set can’t have more than one after.")); + return Err(Error::Other("Set can’t have more than one after.").into()); } set.after = Some(child.text()); } else if child.is("before", ns::RSM) { if set.before.is_some() { - return Err(Error::ParseError("Set can’t have more than one before.")); + return Err(Error::Other("Set can’t have more than one before.").into()); } set.before = Some(child.text()); } else if child.is("index", ns::RSM) { if set.index.is_some() { - return Err(Error::ParseError("Set can’t have more than one index.")); + return Err(Error::Other("Set can’t 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 { - return Err(Error::ParseError("Unknown child in set element.")); + return Err(Error::Other("Unknown child in set element.").into()); } } Ok(set) @@ -111,9 +111,9 @@ pub struct SetResult { } impl TryFrom for SetResult { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "set", RSM, "RSM set"); let mut set = SetResult { first: None, @@ -124,22 +124,22 @@ impl TryFrom for SetResult { for child in elem.children() { if child.is("first", ns::RSM) { if set.first.is_some() { - return Err(Error::ParseError("Set can’t have more than one first.")); + return Err(Error::Other("Set can’t have more than one first.").into()); } set.first_index = get_attr!(child, "index", Option); set.first = Some(child.text()); } else if child.is("last", ns::RSM) { if set.last.is_some() { - return Err(Error::ParseError("Set can’t have more than one last.")); + return Err(Error::Other("Set can’t have more than one last.").into()); } set.last = Some(child.text()); } else if child.is("count", ns::RSM) { if set.count.is_some() { - return Err(Error::ParseError("Set can’t have more than one count.")); + return Err(Error::Other("Set can’t 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 { - return Err(Error::ParseError("Unknown child in set element.")); + return Err(Error::Other("Unknown child in set element.").into()); } } Ok(set) @@ -215,7 +215,7 @@ mod tests { .unwrap(); let error = SetQuery::try_from(elem.clone()).unwrap_err(); let returned_elem = match error { - Error::TypeMismatch(_, _, elem) => elem, + FromElementError::Mismatch(elem) => elem, _ => panic!(), }; assert_eq!(elem, returned_elem); @@ -225,7 +225,7 @@ mod tests { .unwrap(); let error = SetResult::try_from(elem.clone()).unwrap_err(); let returned_elem = match error { - Error::TypeMismatch(_, _, elem) => elem, + FromElementError::Mismatch(elem) => elem, _ => panic!(), }; assert_eq!(elem, returned_elem); @@ -238,7 +238,7 @@ mod tests { .unwrap(); let error = SetQuery::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in set element."); @@ -248,7 +248,7 @@ mod tests { .unwrap(); let error = SetResult::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in set element."); diff --git a/parsers/src/rtt.rs b/parsers/src/rtt.rs index 02fcec35aa3497fac0511cd0aeb3f1060be5de77..16992fd7425b23863b4afdb5352215dc50e0cd11 100644 --- a/parsers/src/rtt.rs +++ b/parsers/src/rtt.rs @@ -5,9 +5,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::ns; -use crate::util::error::Error; use crate::util::text_node_codecs::{Codec, OptionalCodec, Text}; use crate::Element; +use xso::error::{Error, FromElementError}; generate_attribute!( /// Events for real-time text. @@ -49,7 +49,7 @@ impl TryFrom for Insert { fn try_from(action: Action) -> Result { match action { 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 for Erase { - type Error = Error; - fn try_from(elem: Element) -> Result { + type Error = FromElementError; + fn try_from(elem: Element) -> Result { check_self!(elem, "e", RTT); check_no_unknown_attributes!(elem, "e", ["p", "n"]); let pos = get_attr!(elem, "p", Option); @@ -105,7 +105,7 @@ impl TryFrom for Erase { fn try_from(action: Action) -> Result { match action { 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 for Wait { fn try_from(action: Action) -> Result { match action { 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 for Action { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { match elem.name() { "t" => Insert::try_from(elem).map(Action::Insert), "e" => Erase::try_from(elem).map(Action::Erase), "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 for Rtt { - type Error = Error; - fn try_from(elem: Element) -> Result { + type Error = FromElementError; + fn try_from(elem: Element) -> Result { check_self!(elem, "rtt", RTT); check_no_unknown_attributes!(elem, "rtt", ["seq", "event", "id"]); @@ -216,7 +216,7 @@ impl TryFrom for Rtt { let mut actions = Vec::new(); for child in elem.children() { 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())?); } diff --git a/parsers/src/sasl.rs b/parsers/src/sasl.rs index b15cd6c83b1483f9e57e68cc61bb8e3ce583aa9c..ace33a450d9b2c846745277a2aa4264ece4f442b 100644 --- a/parsers/src/sasl.rs +++ b/parsers/src/sasl.rs @@ -5,10 +5,10 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use crate::ns; -use crate::util::error::Error; use crate::util::text_node_codecs::{Base64, Codec}; use crate::Element; use std::collections::BTreeMap; +use xso::error::{Error, FromElementError}; generate_attribute!( /// The list of available SASL mechanisms. @@ -150,9 +150,9 @@ pub struct Failure { } impl TryFrom for Failure { - type Error = Error; + type Error = FromElementError; - fn try_from(root: Element) -> Result { + fn try_from(root: Element) -> Result { check_self!(root, "failure", SASL); check_no_attributes!(root, "failure"); @@ -165,15 +165,17 @@ impl TryFrom for Failure { check_no_children!(child, "text"); let lang = get_attr!(child, "xml:lang", Default); if texts.insert(lang, child.text()).is_some() { - return Err(Error::ParseError( + return Err(Error::Other( "Text element present twice for the same xml:lang in failure element.", - )); + ) + .into()); } } else if child.has_ns(ns::SASL) { if defined_condition.is_some() { - return Err(Error::ParseError( + return Err(Error::Other( "Failure must not have more than one defined-condition.", - )); + ) + .into()); } check_no_attributes!(child, "defined-condition"); check_no_children!(child, "defined-condition"); @@ -184,11 +186,11 @@ impl TryFrom for Failure { }; defined_condition = Some(condition); } else { - return Err(Error::ParseError("Unknown element in Failure.")); + return Err(Error::Other("Unknown element in Failure.").into()); } } 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 { defined_condition, diff --git a/parsers/src/server_info.rs b/parsers/src/server_info.rs index 61b84b4af38ecbc3df971df6785b9915e62d70e2..d0327e6a372de35c90752f3393b2cfca25bec9fe 100644 --- a/parsers/src/server_info.rs +++ b/parsers/src/server_info.rs @@ -5,7 +5,7 @@ use crate::data_forms::{DataForm, DataFormType, Field, FieldType}; use crate::ns; -use crate::util::error::Error; +use xso::error::Error; /// Structure representing a `http://jabber.org/network/serverinfo` form type. #[derive(Debug, Clone, PartialEq, Default)] @@ -34,15 +34,15 @@ impl TryFrom for ServerInfo { fn try_from(form: DataForm) -> 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)) { - return Err(Error::ParseError("Wrong FORM_TYPE for form.")); + return Err(Error::Other("Wrong FORM_TYPE for form.")); } let mut server_info = ServerInfo::default(); for field in form.fields { 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") { server_info.abuse = field.values; @@ -57,7 +57,7 @@ impl TryFrom for ServerInfo { } else if field.var.as_deref() == Some("support-addresses") { server_info.support = field.values; } else { - return Err(Error::ParseError("Unknown form field var.")); + return Err(Error::Other("Unknown form field var.")); } } diff --git a/parsers/src/stanza_error.rs b/parsers/src/stanza_error.rs index b2c3b7726ea39890231708bbfd9982925ff8aa9d..f274931ee1413cbfba948b3d52efb50abc26e024 100644 --- a/parsers/src/stanza_error.rs +++ b/parsers/src/stanza_error.rs @@ -7,12 +7,12 @@ use crate::message::MessagePayload; use crate::ns; use crate::presence::PresencePayload; -use crate::util::error::Error; use crate::Element; use jid::Jid; use minidom::Node; use std::collections::BTreeMap; use std::convert::TryFrom; +use xso::error::{Error, FromElementError}; generate_attribute!( /// The type of the error. @@ -249,9 +249,9 @@ impl StanzaError { } impl TryFrom for StanzaError { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "error", DEFAULT_NS); // 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. @@ -273,15 +273,16 @@ impl TryFrom for StanzaError { check_no_unknown_attributes!(child, "text", ["xml:lang"]); let lang = get_attr!(child, "xml:lang", Default); if stanza_error.texts.insert(lang, child.text()).is_some() { - return Err(Error::ParseError( - "Text element present twice for the same xml:lang.", - )); + return Err( + Error::Other("Text element present twice for the same xml:lang.").into(), + ); } } else if child.has_ns(ns::XMPP_STANZAS) { if defined_condition.is_some() { - return Err(Error::ParseError( + return Err(Error::Other( "Error must not have more than one defined-condition.", - )); + ) + .into()); } check_no_children!(child, "defined-condition"); check_no_attributes!(child, "defined-condition"); @@ -297,15 +298,15 @@ impl TryFrom for StanzaError { defined_condition = Some(condition); } else { if stanza_error.other.is_some() { - return Err(Error::ParseError( - "Error must not have more than one other element.", - )); + return Err( + Error::Other("Error must not have more than one other element.").into(), + ); } stanza_error.other = Some(child.clone()); } } 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) } @@ -369,7 +370,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'type' missing."); @@ -384,10 +385,10 @@ mod tests { .unwrap(); let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::TextParseError(string)) => string, _ => panic!(), }; - assert_eq!(message, "Unknown value for 'type' attribute."); + assert_eq!(message.to_string(), "Unknown value for 'type' attribute."); } #[test] @@ -402,7 +403,7 @@ mod tests { .unwrap(); let error = StanzaError::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Error must have a defined-condition."); diff --git a/parsers/src/stanza_id.rs b/parsers/src/stanza_id.rs index 0ee7da95078ce979b1671d607817bfabcf395320..d3911e0444fb7bed8ae48d1de17f0f1f4e58926f 100644 --- a/parsers/src/stanza_id.rs +++ b/parsers/src/stanza_id.rs @@ -37,9 +37,9 @@ impl MessagePayload for OriginId {} #[cfg(test)] mod tests { use super::*; - use crate::util::error::Error; use crate::Element; use jid::BareJid; + use xso::error::{Error, FromElementError}; #[cfg(target_pointer_width = "32")] #[test] @@ -78,7 +78,7 @@ mod tests { .unwrap(); let error = StanzaId::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Unknown child in stanza-id element."); @@ -89,7 +89,7 @@ mod tests { let elem: Element = "".parse().unwrap(); let error = StanzaId::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'id' missing."); @@ -102,7 +102,7 @@ mod tests { .unwrap(); let error = StanzaId::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Required attribute 'by' missing."); diff --git a/parsers/src/time.rs b/parsers/src/time.rs index ef9a42c98d90935ad2248befbb590c4d2fc07ee0..7f100331f2a19c83230475e80edeebfca996663f 100644 --- a/parsers/src/time.rs +++ b/parsers/src/time.rs @@ -7,10 +7,10 @@ use crate::date::DateTime; use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; -use crate::util::error::Error; use crate::Element; use chrono::FixedOffset; use std::str::FromStr; +use xso::error::{Error, FromElementError}; generate_empty_element!( /// An entity time query. @@ -28,9 +28,9 @@ pub struct TimeResult(pub DateTime); impl IqResultPayload for TimeResult {} impl TryFrom for TimeResult { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "time", TIME); check_no_attributes!(elem, "time"); @@ -40,33 +40,34 @@ impl TryFrom for TimeResult { for child in elem.children() { if child.is("tzo", ns::TIME) { 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_attributes!(child, "tzo"); // TODO: Add a FromStr implementation to FixedOffset to avoid this hack. 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()); } else if child.is("utc", ns::TIME) { 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_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) { 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); } 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 utc = utc.ok_or(Error::ParseError("Missing utc child in time element."))?; + let tzo = tzo.ok_or(Error::Other("Missing tzo child in time element."))?; + let utc = utc.ok_or(Error::Other("Missing utc child in time element."))?; let date = utc.with_timezone(tzo); Ok(TimeResult(date)) diff --git a/parsers/src/tune.rs b/parsers/src/tune.rs index 8b1ae5d59875e138449d4c3ad6e1c0e26d085189..5c91051e06f14fce8359a70b67babd4f39af83b0 100644 --- a/parsers/src/tune.rs +++ b/parsers/src/tune.rs @@ -6,8 +6,8 @@ use crate::ns; use crate::pubsub::PubSubPayload; -use crate::util::error::Error; use crate::Element; +use xso::error::{Error, FromElementError}; generate_elem_id!( /// The artist or performer of the song or piece. @@ -106,9 +106,9 @@ impl Tune { } impl TryFrom for Tune { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "tune", TUNE); check_no_attributes!(elem, "tune"); @@ -116,41 +116,41 @@ impl TryFrom for Tune { for child in elem.children() { if child.is("artist", ns::TUNE) { if tune.artist.is_some() { - return Err(Error::ParseError("Tune can’t have more than one artist.")); + return Err(Error::Other("Tune can’t have more than one artist.").into()); } tune.artist = Some(Artist::try_from(child.clone())?); } else if child.is("length", ns::TUNE) { if tune.length.is_some() { - return Err(Error::ParseError("Tune can’t have more than one length.")); + return Err(Error::Other("Tune can’t have more than one length.").into()); } tune.length = Some(Length::try_from(child.clone())?); } else if child.is("rating", ns::TUNE) { if tune.rating.is_some() { - return Err(Error::ParseError("Tune can’t have more than one rating.")); + return Err(Error::Other("Tune can’t have more than one rating.").into()); } tune.rating = Some(Rating::try_from(child.clone())?); } else if child.is("source", ns::TUNE) { if tune.source.is_some() { - return Err(Error::ParseError("Tune can’t have more than one source.")); + return Err(Error::Other("Tune can’t have more than one source.").into()); } tune.source = Some(Source::try_from(child.clone())?); } else if child.is("title", ns::TUNE) { if tune.title.is_some() { - return Err(Error::ParseError("Tune can’t have more than one title.")); + return Err(Error::Other("Tune can’t have more than one title.").into()); } tune.title = Some(Title::try_from(child.clone())?); } else if child.is("track", ns::TUNE) { if tune.track.is_some() { - return Err(Error::ParseError("Tune can’t have more than one track.")); + return Err(Error::Other("Tune can’t have more than one track.").into()); } tune.track = Some(Track::try_from(child.clone())?); } else if child.is("uri", ns::TUNE) { if tune.uri.is_some() { - return Err(Error::ParseError("Tune can’t have more than one uri.")); + return Err(Error::Other("Tune can’t have more than one uri.").into()); } tune.uri = Some(Uri::try_from(child.clone())?); } else { - return Err(Error::ParseError("Unknown element in User Tune.")); + return Err(Error::Other("Unknown element in User Tune.").into()); } } diff --git a/parsers/src/util/error.rs b/parsers/src/util/error.rs deleted file mode 100644 index 377d666c86eeafeb3778de57c59cc34d4912527e..0000000000000000000000000000000000000000 --- a/parsers/src/util/error.rs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2017-2018 Emmanuel Gil Peyrot -// -// 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 for Error { - fn from(err: base64::DecodeError) -> Error { - Error::Base64Error(err) - } -} - -impl From for Error { - fn from(err: std::num::ParseIntError) -> Error { - Error::ParseIntError(err) - } -} - -impl From for Error { - fn from(err: std::string::ParseError) -> Error { - Error::ParseStringError(err) - } -} - -impl From for Error { - fn from(err: std::net::AddrParseError) -> Error { - Error::ParseAddrError(err) - } -} - -impl From for Error { - fn from(err: jid::Error) -> Error { - Error::JidParseError(err) - } -} - -impl From for Error { - fn from(err: chrono::ParseError) -> Error { - Error::ChronoParseError(err) - } -} - -impl From 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)), - } - } -} diff --git a/parsers/src/util/macros.rs b/parsers/src/util/macros.rs index e17d89ed4c441e974ea3ec91540036e8d3363d8a..daa3570875b81da3131f3d53f3a32c0a0a4495f1 100644 --- a/parsers/src/util/macros.rs +++ b/parsers/src/util/macros.rs @@ -6,7 +6,13 @@ macro_rules! get_attr { ($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) => { match $elem.attr($attr) { @@ -25,30 +31,27 @@ macro_rules! get_attr { match $elem.attr($attr) { Some($value) => $func, None => { - return Err(crate::util::error::Error::ParseError(concat!( - "Required attribute '", - $attr, - "' missing." - ))); + return Err(xso::error::Error::Other( + concat!("Required attribute '", $attr, "' missing.").into(), + ) + .into()); } } }; ($elem:ident, $attr:tt, RequiredNonEmpty, $value:ident, $func:expr) => { match $elem.attr($attr) { Some("") => { - return Err(crate::util::error::Error::ParseError(concat!( - "Required attribute '", - $attr, - "' must not be empty." - ))); + return Err(xso::error::Error::Other( + concat!("Required attribute '", $attr, "' must not be empty.").into(), + ) + .into()); } Some($value) => $func, None => { - return Err(crate::util::error::Error::ParseError(concat!( - "Required attribute '", - $attr, - "' missing." - ))); + return Err(xso::error::Error::Other( + concat!("Required attribute '", $attr, "' missing.").into(), + ) + .into()); } } }; @@ -71,11 +74,11 @@ macro_rules! generate_attribute { ),+ } impl ::std::str::FromStr for $elem { - type Err = crate::util::error::Error; - fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { + type Err = xso::error::Error; + fn from_str(s: &str) -> Result<$elem, xso::error::Error> { Ok(match s { $($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 { - type Err = crate::util::error::Error; - fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { + type Err = xso::error::Error; + fn from_str(s: &str) -> Result<$elem, xso::error::Error> { Ok(match s { $($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, } impl ::std::str::FromStr for $elem { - type Err = crate::util::error::Error; - fn from_str(s: &str) -> Result { + type Err = xso::error::Error; + fn from_str(s: &str) -> Result { Ok(match s { $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, } impl ::std::str::FromStr for $elem { - type Err = crate::util::error::Error; - fn from_str(s: &str) -> Result { + type Err = xso::error::Error; + fn from_str(s: &str) -> Result { Ok(match s { "true" | "1" => $elem::True, "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)] pub struct $elem(pub $type); impl ::std::str::FromStr for $elem { - type Err = crate::util::error::Error; - fn from_str(s: &str) -> Result { - Ok($elem($type::from_str(s)?)) + type Err = xso::error::Error; + fn from_str(s: &str) -> Result { + Ok($elem($type::from_str(s).map_err(xso::error::Error::text_parse_error)?)) } } impl ::minidom::IntoAttributeValue for $elem { @@ -229,14 +232,14 @@ macro_rules! generate_element_enum { ),+ } impl ::std::convert::TryFrom for $elem { - type Error = crate::util::error::Error; - fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { + type Error = xso::error::FromElementError; + fn try_from(elem: crate::Element) -> Result<$elem, xso::error::FromElementError> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); Ok(match elem.name() { $($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 for $elem { - type Error = crate::util::error::Error; - fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { + type Error = xso::error::FromElementError; + fn try_from(elem: crate::Element) -> Result<$elem, xso::error::FromElementError> { check_ns_only!(elem, $name, $ns); check_no_children!(elem, $name); check_no_unknown_attributes!(elem, $name, [$attr]); Ok(match get_attr!(elem, $attr, Required) { $($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) => { if !$elem.is($name, crate::ns::$ns) { - return Err(crate::util::error::Error::TypeMismatch( - $name, - crate::ns::$ns, - $elem, - )); + return Err(xso::error::FromElementError::Mismatch($elem)); } }; } @@ -309,11 +308,10 @@ macro_rules! check_child { }; ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => { if !$elem.is($name, crate::ns::$ns) { - return Err(crate::util::error::Error::ParseError(concat!( - "This is not a ", - $pretty_name, - " element." - ))); + return Err(xso::error::Error::Other( + concat!("This is not a ", $pretty_name, " element.").into(), + ) + .into()); } }; } @@ -321,11 +319,10 @@ macro_rules! check_child { macro_rules! check_ns_only { ($elem:ident, $name:tt, $ns:ident) => { if !$elem.has_ns(crate::ns::$ns) { - 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(), + ) + .into()); } }; } @@ -334,11 +331,10 @@ macro_rules! check_no_children { ($elem:ident, $name:tt) => { #[cfg(not(feature = "disable-validation"))] for _ in $elem.children() { - 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(), + ) + .into()); } }; } @@ -347,11 +343,10 @@ macro_rules! check_no_attributes { ($elem:ident, $name:tt) => { #[cfg(not(feature = "disable-validation"))] for _ in $elem.attrs() { - 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(), + ) + .into()); } }; } @@ -365,7 +360,7 @@ macro_rules! check_no_unknown_attributes { 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; impl ::std::convert::TryFrom 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_no_children!(elem, $name); check_no_attributes!(elem, $name); @@ -402,8 +397,8 @@ macro_rules! generate_id { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub String); impl ::std::str::FromStr for $elem { - type Err = crate::util::error::Error; - fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { + type Err = xso::error::Error; + fn from_str(s: &str) -> Result<$elem, xso::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } @@ -420,8 +415,8 @@ macro_rules! generate_elem_id { ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => ( generate_elem_id!($(#[$meta])* $elem, $name, $ns, String); impl ::std::str::FromStr for $elem { - type Err = crate::util::error::Error; - fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> { + type Err = xso::error::Error; + fn from_str(s: &str) -> Result<$elem, xso::error::Error> { // TODO: add a way to parse that differently when needed. Ok($elem(String::from(s))) } @@ -432,13 +427,13 @@ macro_rules! generate_elem_id { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $elem(pub $type); impl ::std::convert::TryFrom for $elem { - type Error = crate::util::error::Error; - fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> { + type Error = xso::error::FromElementError; + fn try_from(elem: crate::Element) -> Result<$elem, xso::error::FromElementError> { check_self!(elem, $name, $ns); check_no_children!(elem, $name); check_no_attributes!(elem, $name); // 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 { @@ -507,7 +502,7 @@ macro_rules! do_parse { Ok($elem.text()) }; ($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) => { if $temp.is_some() { - Err(crate::util::error::Error::ParseError(concat!( - "Element ", - $parent_name, - " must not have more than one ", - $name, - " child." - ))) + Err(xso::error::Error::Other( + concat!( + "Element ", + $parent_name, + " must not have more than one ", + $name, + " child." + ) + .into(), + )) } else { match do_parse!($elem, $constructor) { Ok(v) => { @@ -539,13 +537,16 @@ macro_rules! do_parse_elem { }; ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => { if $temp.is_some() { - Err(crate::util::error::Error::ParseError(concat!( - "Element ", - $parent_name, - " must not have more than one ", - $name, - " child." - ))) + Err(xso::error::Error::Other( + concat!( + "Element ", + $parent_name, + " must not have more than one ", + $name, + " child." + ) + .into(), + )) } else { match do_parse!($elem, $constructor) { Ok(v) => { @@ -558,13 +559,16 @@ macro_rules! do_parse_elem { }; ($temp:ident: Present = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => { if $temp { - Err(crate::util::error::Error::ParseError(concat!( - "Element ", - $parent_name, - " must not have more than one ", - $name, - " child." - ))) + Err(xso::error::Error::Other( + concat!( + "Element ", + $parent_name, + " must not have more than one ", + $name, + " child." + ) + .into(), + )) } else { $temp = true; Ok(()) @@ -580,13 +584,9 @@ macro_rules! finish_parse_elem { $temp }; ($temp:ident: Required = $name:tt, $parent_name:tt) => { - $temp.ok_or(crate::util::error::Error::ParseError(concat!( - "Missing child ", - $name, - " in ", - $parent_name, - " element." - )))? + $temp.ok_or(xso::error::Error::Other( + concat!("Missing child ", $name, " in ", $parent_name, " element.").into(), + ))? }; ($temp:ident: Present = $name:tt, $parent_name:tt) => { $temp @@ -694,9 +694,9 @@ macro_rules! generate_element { } impl ::std::convert::TryFrom 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_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) { match do_parse_elem!($child_ident: $coucou = $child_constructor => residual, $child_name, $name) { Ok(()) => continue, - Err(other) => return Err(other), + Err(other) => return Err(other.into()), } } else { 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 { $( @@ -787,17 +787,15 @@ macro_rules! assert_size ( macro_rules! impl_pubsub_item { ($item:ident, $ns:ident) => { impl ::std::convert::TryFrom 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_no_unknown_attributes!(elem, "item", ["id", "publisher"]); let mut payloads = elem.take_contents_as_children().collect::>(); let payload = payloads.pop(); if !payloads.is_empty() { - return Err(Error::ParseError( - "More than a single payload in item element.", - )); + return Err(Error::Other("More than a single payload in item element.").into()); } Ok($item(crate::pubsub::Item { id: get_attr!(elem, "id", Option), diff --git a/parsers/src/util/mod.rs b/parsers/src/util/mod.rs index 8e80667ec4be84c71ece44e37f8dcc7bbc80b551..75e1fb86346099f13a229da7ecbde992e4fa0a2e 100644 --- a/parsers/src/util/mod.rs +++ b/parsers/src/util/mod.rs @@ -4,9 +4,6 @@ // 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/. -/// Error type returned by every parser on failure. -pub mod error; - /// Various helpers. pub(crate) mod text_node_codecs; diff --git a/parsers/src/util/text_node_codecs.rs b/parsers/src/util/text_node_codecs.rs index e45dec2f063c278e49bf8c699ece2650f93db110..0ceee3901975e9f022b33cf802decd10016b6999 100644 --- a/parsers/src/util/text_node_codecs.rs +++ b/parsers/src/util/text_node_codecs.rs @@ -4,10 +4,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -use crate::util::error::Error; use base64::{engine::general_purpose::STANDARD as Base64Engine, Engine}; use jid::Jid; use std::str::FromStr; +use xso::error::Error; /// A trait for codecs that can decode and encode text nodes. pub trait Codec { @@ -70,7 +70,7 @@ where match s.trim() { // TODO: This error message can be a bit opaque when used // 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.", )), trimmed => T::decode(trimmed), @@ -89,7 +89,7 @@ impl Codec for Base64 { type Decoded = Vec; fn decode(s: &str) -> Result, Error> { - Ok(Base64Engine.decode(s)?) + Base64Engine.decode(s).map_err(Error::text_parse_error) } fn encode(decoded: &Vec) -> Option { @@ -109,7 +109,7 @@ impl Codec for WhitespaceAwareBase64 { .filter(|ch| *ch != ' ' && *ch != '\n' && *ch != '\t') .collect(); - Ok(Base64Engine.decode(s)?) + Base64Engine.decode(s).map_err(Error::text_parse_error) } fn encode(decoded: &Self::Decoded) -> Option { @@ -125,12 +125,13 @@ impl Codec for FixedHex { fn decode(s: &str) -> Result { if s.len() != 2 * N { - return Err(Error::ParseError("Invalid length")); + return Err(Error::Other("Invalid length")); } let mut bytes = [0u8; 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) @@ -154,7 +155,8 @@ impl Codec for ColonSeparatedHex { fn decode(s: &str) -> Result { let mut bytes = vec![]; 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() { assert_eq!(&s[3 * i + 2..3 * i + 3], ":"); } @@ -179,7 +181,7 @@ impl Codec for JidCodec { type Decoded = Jid; fn decode(s: &str) -> Result { - Ok(Jid::from_str(s)?) + Jid::from_str(s).map_err(Error::text_parse_error) } fn encode(jid: &Jid) -> Option { @@ -205,28 +207,28 @@ mod tests { // What if we give it a string that's too long? 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? 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? 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. let err = FixedHex::<3>::decode("0:f:EF").unwrap_err(); assert_eq!( err.to_string(), - "integer parsing error: invalid digit found in string" + "text parse error: invalid digit found in string" ); // No non-hex character allowed. let err = FixedHex::<3>::decode("01defg").unwrap_err(); assert_eq!( err.to_string(), - "integer parsing error: invalid digit found in string" + "text parse error: invalid digit found in string" ); } } diff --git a/parsers/src/vcard.rs b/parsers/src/vcard.rs index 7f4150782eb2dd84d4824adb1a35e3b9be634db6..96241a52b2a5aab26f4a301857aded8a8fd68624 100644 --- a/parsers/src/vcard.rs +++ b/parsers/src/vcard.rs @@ -55,14 +55,12 @@ pub struct VCard { } impl TryFrom for VCard { - type Error = crate::util::error::Error; + type Error = xso::error::Error; fn try_from(value: Element) -> Result { // Check that the root element is if !value.is("vCard", ns::VCARD) { - return Err(Error::ParseError( - "Root element is not ", - )); + return Err(Error::Other("Root element is not ").into()); } // Parse the element, if any. diff --git a/parsers/src/xhtml.rs b/parsers/src/xhtml.rs index f89b4e2df20642444ad9dcdc7c8656132dbac29c..7c99afe9bba3a4fe706a1d6a4274054272059733 100644 --- a/parsers/src/xhtml.rs +++ b/parsers/src/xhtml.rs @@ -6,9 +6,9 @@ use crate::message::MessagePayload; use crate::ns; -use crate::util::error::Error; use minidom::{Element, Node}; use std::collections::HashMap; +use xso::error::{Error, FromElementError}; // TODO: Use a proper lang type. type Lang = String; @@ -60,9 +60,9 @@ impl XhtmlIm { impl MessagePayload for XhtmlIm {} impl TryFrom for XhtmlIm { - type Error = Error; + type Error = FromElementError; - fn try_from(elem: Element) -> Result { + fn try_from(elem: Element) -> Result { check_self!(elem, "html", XHTML_IM); check_no_attributes!(elem, "html"); @@ -75,13 +75,14 @@ impl TryFrom for XhtmlIm { match bodies.insert(lang, body) { None => (), Some(_) => { - return Err(Error::ParseError( + return Err(Error::Other( "Two identical language bodies found in XHTML-IM.", - )) + ) + .into()) } } } 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(); let error = XhtmlIm::try_from(elem).unwrap_err(); let message = match error { - Error::ParseError(string) => string, + FromElementError::Invalid(Error::Other(string)) => string, _ => panic!(), }; assert_eq!(message, "Two identical language bodies found in XHTML-IM."); diff --git a/xso/src/error.rs b/xso/src/error.rs index c2340b88df19fa44b0068f0c94b7418ae2b446ac..d8cebf8c9c6ca0a5e6e03e76063827e5f03d221b 100644 --- a/xso/src/error.rs +++ b/xso/src/error.rs @@ -23,7 +23,7 @@ pub enum Error { TextParseError(Box), /// Generic, unspecified other error. - Other(Box), + Other(&'static str), /// An element header did not match an expected element. /// @@ -33,6 +33,16 @@ pub enum Error { 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(e: T) -> Self { + Self::TextParseError(Box::new(e)) + } +} + impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { @@ -121,3 +131,75 @@ impl std::error::Error for FromEventsError { } } } + +impl From for Result { + fn from(other: Error) -> Self { + Self::Err(other) + } +} + +/// Error returned by the `TryFrom` 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> for FromElementError { + fn from(other: Result) -> Self { + match other { + Ok(v) => Self::Mismatch(v), + Err(e) => Self::Invalid(e), + } + } +} + +impl From for FromElementError { + fn from(other: Error) -> Self { + Self::Invalid(other) + } +} + +impl From for Error { + fn from(other: FromElementError) -> Self { + match other { + FromElementError::Invalid(e) => e, + FromElementError::Mismatch(..) => Self::TypeMismatch, + } + } +} + +impl From for FromElementError { + fn from(other: core::convert::Infallible) -> Self { + match other {} + } +} diff --git a/xso/src/lib.rs b/xso/src/lib.rs index cd4b5f9f42cfdb68a4ffe1fcb847214b24782a99..f18ae7fd4891d0cd8450b9548f716ff8bbebac16 100644 --- a/xso/src/lib.rs +++ b/xso/src/lib.rs @@ -138,3 +138,38 @@ pub fn transform(from: F) -> Result( + from: minidom::Element, +) -> Result { + 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") +} diff --git a/xso/src/minidom_compat.rs b/xso/src/minidom_compat.rs index 9bd5ce40f849d25c814d822b6027b9bde6b1f0fb..6c8164e33d7609b2ab5da8567714fe658f9a67bf 100644 --- a/xso/src/minidom_compat.rs +++ b/xso/src/minidom_compat.rs @@ -55,7 +55,7 @@ enum IntoEventsInner { // NOTE to developers: The limitations are not fully trivial to overcome: // the attributes use a BTreeMap internally, which does not offer a `drain` // 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 namespace = Namespace::from(el.ns());