parsers: use Error type from xso

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

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

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

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

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

View file

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

View file

@ -6,7 +6,13 @@
macro_rules! get_attr {
($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<Self, crate::util::error::Error> {
type Err = xso::error::Error;
fn from_str(s: &str) -> Result<Self, xso::error::Error> {
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<Self, crate::util::error::Error> {
type Err = xso::error::Error;
fn from_str(s: &str) -> Result<Self, xso::error::Error> {
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<Self, crate::util::error::Error> {
Ok($elem($type::from_str(s)?))
type Err = xso::error::Error;
fn from_str(s: &str) -> Result<Self, xso::error::Error> {
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<crate::Element> 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<crate::Element> 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<crate::Element> for $elem {
type Error = crate::util::error::Error;
type Error = xso::error::FromElementError;
fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
fn try_from(elem: crate::Element) -> Result<$elem, xso::error::FromElementError> {
check_self!(elem, $name, $ns);
check_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<crate::Element> 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<crate::Element> for $elem {
type Error = crate::util::error::Error;
type Error = xso::error::FromElementError;
fn try_from(mut elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
fn try_from(mut elem: crate::Element) -> Result<$elem, xso::error::FromElementError> {
check_self!(elem, $name, $ns);
check_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<crate::Element> for $item {
type Error = Error;
type Error = FromElementError;
fn try_from(mut elem: crate::Element) -> Result<$item, Error> {
fn try_from(mut elem: crate::Element) -> Result<$item, FromElementError> {
check_self!(elem, "item", $ns);
check_no_unknown_attributes!(elem, "item", ["id", "publisher"]);
let mut payloads = elem.take_contents_as_children().collect::<Vec<_>>();
let payload = payloads.pop();
if !payloads.is_empty() {
return Err(Error::ParseError(
"More than a single payload in item element.",
));
return Err(Error::Other("More than a single payload in item element.").into());
}
Ok($item(crate::pubsub::Item {
id: get_attr!(elem, "id", Option),

View file

@ -4,9 +4,6 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// 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;

View file

@ -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<u8>;
fn decode(s: &str) -> Result<Vec<u8>, Error> {
Ok(Base64Engine.decode(s)?)
Base64Engine.decode(s).map_err(Error::text_parse_error)
}
fn encode(decoded: &Vec<u8>) -> Option<String> {
@ -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<String> {
@ -125,12 +125,13 @@ impl<const N: usize> Codec for FixedHex<N> {
fn decode(s: &str) -> Result<Self::Decoded, Error> {
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<Self::Decoded, Error> {
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<Jid, Error> {
Ok(Jid::from_str(s)?)
Jid::from_str(s).map_err(Error::text_parse_error)
}
fn encode(jid: &Jid) -> Option<String> {
@ -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"
);
}
}