use core::convert::Infallible;
use core::fmt;
#[cfg(feature = "std")]
use std::error::Error as StdError;
#[cfg(all(not(feature = "std"), feature = "newer-rust-version"))]
if_rust_version::if_rust_version! {
>= 1.81 {
use core::error::Error as StdError;
}
}
#[cfg(feature = "std")]
macro_rules! if_std_error {
({ $($if_yes:tt)* } $(else { $($if_not:tt)* })?) => {
#[cfg_attr(docsrs, doc(cfg(any(feature = "std", all(feature = "newer-rust-version", rust_version = ">= 1.81.0")))))]
$($if_yes)*
}
}
#[cfg(all(not(feature = "std"), feature = "newer-rust-version"))]
macro_rules! if_std_error {
({ $($if_yes:tt)* } $(else { $($if_not:tt)* })?) => {
if_rust_version::if_rust_version! {
>= 1.81 {
#[cfg_attr(docsrs, doc(cfg(any(feature = "std", all(feature = "newer-rust-version", rust_version = ">= 1.81.0")))))]
$($if_yes)*
} $(else { $($if_not)* })?
}
}
}
#[cfg(all(not(feature = "std"), not(feature = "newer-rust-version")))]
macro_rules! if_std_error {
({ $($if_yes:tt)* } $(else { $($if_not:tt)* })?) => {
$($($if_not)*)?
}
}
macro_rules! write_err {
($writer:expr, $string:literal $(, $args:expr)*; $source:expr) => {
{
if_std_error! {
{
{
let _ = &$source; write!($writer, $string $(, $args)*)
}
} else {
{
write!($writer, concat!($string, ": {}") $(, $args)*, $source)
}
}
}
}
}
}
pub(crate) use write_err;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DecodeVariableLengthBytesError {
InvalidChar(InvalidCharError),
OddLengthString(OddLengthStringError),
}
impl DecodeVariableLengthBytesError {
#[must_use]
#[inline]
pub fn offset(self, by_bytes: usize) -> Self {
use DecodeVariableLengthBytesError as E;
match self {
E::InvalidChar(e) => E::InvalidChar(e.offset(by_bytes)),
E::OddLengthString(e) => E::OddLengthString(e),
}
}
}
impl From<Infallible> for DecodeVariableLengthBytesError {
#[inline]
fn from(never: Infallible) -> Self { match never {} }
}
impl fmt::Display for DecodeVariableLengthBytesError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use DecodeVariableLengthBytesError as E;
match *self {
E::InvalidChar(ref e) => write_err!(f, "failed to decode hex"; e),
E::OddLengthString(ref e) => write_err!(f, "failed to decode hex"; e),
}
}
}
if_std_error! {{
impl StdError for DecodeVariableLengthBytesError {
#[inline]
fn source(&self) -> Option<&(dyn StdError + 'static)> {
use DecodeVariableLengthBytesError as E;
match *self {
E::InvalidChar(ref e) => Some(e),
E::OddLengthString(ref e) => Some(e),
}
}
}
}}
impl From<InvalidCharError> for DecodeVariableLengthBytesError {
#[inline]
fn from(e: InvalidCharError) -> Self { Self::InvalidChar(e) }
}
impl From<OddLengthStringError> for DecodeVariableLengthBytesError {
#[inline]
fn from(e: OddLengthStringError) -> Self { Self::OddLengthString(e) }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidCharError {
pub(crate) invalid: u8,
pub(crate) pos: usize,
}
impl From<Infallible> for InvalidCharError {
#[inline]
fn from(never: Infallible) -> Self { match never {} }
}
impl InvalidCharError {
#[inline]
pub(crate) fn invalid_char(&self) -> u8 { self.invalid }
#[inline]
pub fn pos(&self) -> usize { self.pos }
#[must_use]
#[inline]
pub fn offset(mut self, by_bytes: usize) -> Self {
self.pos += by_bytes;
self
}
}
impl fmt::Display for InvalidCharError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
struct Format<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result>(F);
impl<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result> fmt::Display for Format<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0(f) }
}
let which;
let which: &dyn fmt::Display = match self.pos() {
0 => &"1st",
1 => &"2nd",
2 => &"3rd",
pos => {
which = Format(move |f| write!(f, "{}th", pos + 1));
&which
}
};
let chr_ascii;
let chr_non_ascii;
let invalid_char = self.invalid_char();
let chr: &dyn fmt::Display = if self.invalid_char().is_ascii() {
chr_ascii = Format(move |f| write!(f, "{:?}", invalid_char as char));
&chr_ascii
} else {
chr_non_ascii = Format(move |f| write!(f, "{:#02x}", invalid_char));
&chr_non_ascii
};
write!(f, "the {} character, {}, is not a valid hex digit", which, chr)
}
}
if_std_error! {{
impl StdError for InvalidCharError {}
}}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OddLengthStringError {
pub(crate) len: usize,
}
impl From<Infallible> for OddLengthStringError {
#[inline]
fn from(never: Infallible) -> Self { match never {} }
}
impl OddLengthStringError {
#[inline]
pub fn length(&self) -> usize { self.len }
}
impl fmt::Display for OddLengthStringError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.length() == 1 {
write!(f, "the hex string is 1 byte long which is not an even number")
} else {
write!(f, "the hex string is {} bytes long which is not an even number", self.length())
}
}
}
if_std_error! {{
impl StdError for OddLengthStringError {}
}}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DecodeFixedLengthBytesError {
InvalidChar(InvalidCharError),
InvalidLength(InvalidLengthError),
}
impl DecodeFixedLengthBytesError {
#[must_use]
#[inline]
pub fn offset(self, by_bytes: usize) -> Self {
use DecodeFixedLengthBytesError as E;
match self {
E::InvalidChar(e) => E::InvalidChar(e.offset(by_bytes)),
E::InvalidLength(e) => E::InvalidLength(e),
}
}
}
impl From<Infallible> for DecodeFixedLengthBytesError {
#[inline]
fn from(never: Infallible) -> Self { match never {} }
}
impl fmt::Display for DecodeFixedLengthBytesError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use DecodeFixedLengthBytesError as E;
match *self {
E::InvalidChar(ref e) => write_err!(f, "failed to parse hex"; e),
E::InvalidLength(ref e) => write_err!(f, "failed to parse hex"; e),
}
}
}
if_std_error! {{
impl StdError for DecodeFixedLengthBytesError {
#[inline]
fn source(&self) -> Option<&(dyn StdError + 'static)> {
use DecodeFixedLengthBytesError as E;
match *self {
E::InvalidChar(ref e) => Some(e),
E::InvalidLength(ref e) => Some(e),
}
}
}
}}
impl From<InvalidCharError> for DecodeFixedLengthBytesError {
#[inline]
fn from(e: InvalidCharError) -> Self { Self::InvalidChar(e) }
}
impl From<InvalidLengthError> for DecodeFixedLengthBytesError {
#[inline]
fn from(e: InvalidLengthError) -> Self { Self::InvalidLength(e) }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidLengthError {
pub(crate) expected: usize,
pub(crate) invalid: usize,
}
impl From<Infallible> for InvalidLengthError {
#[inline]
fn from(never: Infallible) -> Self { match never {} }
}
impl InvalidLengthError {
#[inline]
pub fn expected_length(&self) -> usize { self.expected }
#[inline]
pub fn invalid_length(&self) -> usize { self.invalid }
}
impl fmt::Display for InvalidLengthError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"the hex string is {} bytes long but exactly {} bytes were required",
self.invalid_length(),
self.expected_length()
)
}
}
if_std_error! {{
impl StdError for InvalidLengthError {}
}}
#[cfg(test)]
#[cfg(feature = "std")]
mod tests {
use super::*;
use crate::{decode_to_array, decode_to_vec};
fn check_source<T: std::error::Error>(error: &T) {
assert!(error.source().is_some());
}
#[cfg(feature = "alloc")]
#[test]
fn invalid_char_error() {
let result = decode_to_vec("12G4");
let error = result.unwrap_err();
if let DecodeVariableLengthBytesError::InvalidChar(e) = error {
assert!(!format!("{}", e).is_empty());
assert_eq!(e.invalid_char(), b'G');
assert_eq!(e.pos(), 2);
} else {
panic!("Expected InvalidCharError");
}
}
#[cfg(feature = "alloc")]
#[test]
fn odd_length_string_error() {
let result = decode_to_vec("123");
let error = result.unwrap_err();
assert!(!format!("{}", error).is_empty());
check_source(&error);
if let DecodeVariableLengthBytesError::OddLengthString(e) = error {
assert!(!format!("{}", e).is_empty());
assert_eq!(e.length(), 3);
} else {
panic!("Expected OddLengthStringError");
}
}
#[test]
fn invalid_length_error() {
let result = decode_to_array::<4>("123");
let error = result.unwrap_err();
assert!(!format!("{}", error).is_empty());
check_source(&error);
if let DecodeFixedLengthBytesError::InvalidLength(e) = error {
assert!(!format!("{}", e).is_empty());
assert_eq!(e.expected_length(), 8);
assert_eq!(e.invalid_length(), 3);
} else {
panic!("Expected InvalidLengthError");
}
}
#[test]
fn to_bytes_error() {
let error =
DecodeVariableLengthBytesError::OddLengthString(OddLengthStringError { len: 7 });
assert!(!format!("{}", error).is_empty());
check_source(&error);
}
#[test]
fn to_array_error() {
let error = DecodeFixedLengthBytesError::InvalidLength(InvalidLengthError {
expected: 8,
invalid: 7,
});
assert!(!format!("{}", error).is_empty());
check_source(&error);
}
}