Skip to content

Commit

Permalink
Hide all errors
Browse files Browse the repository at this point in the history
Hide the internals of all errors by wrapping enum variant internals in
structs.
  • Loading branch information
tcharding committed Nov 6, 2023
1 parent 881c098 commit dee6b08
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 28 deletions.
2 changes: 1 addition & 1 deletion examples/custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ impl<'a> fmt::UpperHex for DisplayALittleBitHexy<'a> {
}

/// Example Error.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
/// Conversion error while parsing hex string.
Conversion(HexToBytesError),
Expand Down
96 changes: 84 additions & 12 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

use core::fmt;

use crate::write_err;

/// Formats error.
///
/// If `std` feature is OFF appends error source (delimited by `: `). We do this because
Expand All @@ -28,18 +30,19 @@ macro_rules! write_err {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum HexToBytesError {
/// Non-hexadecimal character.
InvalidChar(u8),
InvalidChar(InvalidCharError),
/// Purported hex string had odd length.
OddLengthString(usize),
OddLengthString(OddLengthStringError),
}

impl fmt::Display for HexToBytesError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use HexToBytesError::*;

match *self {
InvalidChar(ch) => write!(f, "invalid hex character {}", ch),
OddLengthString(ell) => write!(f, "odd hex string length {}", ell),
InvalidChar(ref e) => write_err!(f, "invalid char, failed to create bytes from hex"; e),
OddLengthString(ref e) =>
write_err!(f, "odd length, failed to create bytes from hex"; e),
}
}
}
Expand All @@ -49,29 +52,75 @@ impl std::error::Error for HexToBytesError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use HexToBytesError::*;

match self {
InvalidChar(_) | OddLengthString(_) => None,
match *self {
InvalidChar(ref e) => Some(e),
OddLengthString(ref e) => Some(e),
}
}
}

/// Hex decoding error.
impl From<InvalidCharError> for HexToBytesError {
#[inline]
fn from(e: InvalidCharError) -> Self { Self::InvalidChar(e) }
}

impl From<OddLengthStringError> for HexToBytesError {
#[inline]
fn from(e: OddLengthStringError) -> Self { Self::OddLengthString(e) }
}

/// Invalid hex character.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct InvalidCharError {
pub(crate) invalid: u8,
}

impl fmt::Display for InvalidCharError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "invalid hex char {}", self.invalid)
}
}

#[cfg(feature = "std")]
impl std::error::Error for InvalidCharError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
}

/// Purported hex string had odd length.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct OddLengthStringError {
pub(crate) len: usize,
}

impl fmt::Display for OddLengthStringError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "odd hex string length {}", self.len)
}
}

#[cfg(feature = "std")]
impl std::error::Error for OddLengthStringError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
}

/// Hex decoding error.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HexToArrayError {
/// Conversion error while parsing hex string.
Conversion(HexToBytesError),
/// Tried to parse fixed-length hash from a string with the wrong length (got, want).
InvalidLength(usize, usize),
InvalidLength(InvalidLengthError),
}

impl fmt::Display for HexToArrayError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use HexToArrayError::*;

match *self {
Conversion(ref e) => crate::write_err!(f, "conversion error"; e),
InvalidLength(got, want) =>
write!(f, "bad hex string length {} (expected {})", got, want),
Conversion(ref e) =>
crate::write_err!(f, "conversion error, failed to create array from hex"; e),
InvalidLength(ref e) =>
write_err!(f, "invalid length, failed to create array from hex"; e),
}
}
}
Expand All @@ -83,7 +132,7 @@ impl std::error::Error for HexToArrayError {

match *self {
Conversion(ref e) => Some(e),
InvalidLength(_, _) => None,
InvalidLength(ref e) => Some(e),
}
}
}
Expand All @@ -92,3 +141,26 @@ impl From<HexToBytesError> for HexToArrayError {
#[inline]
fn from(e: HexToBytesError) -> Self { Self::Conversion(e) }
}

impl From<InvalidLengthError> for HexToArrayError {
#[inline]
fn from(e: InvalidLengthError) -> Self { Self::InvalidLength(e) }
}

/// Tried to parse fixed-length hash from a string with the wrong length.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidLengthError {
pub(crate) expected: usize,
pub(crate) got: usize,
}

impl fmt::Display for InvalidLengthError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "bad hex string length {} (expected {})", self.got, self.expected)
}
}

#[cfg(feature = "std")]
impl std::error::Error for InvalidLengthError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
}
13 changes: 9 additions & 4 deletions src/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use std::io;
#[cfg(all(feature = "core2", not(feature = "std")))]
use core2::io;

use crate::parse::HexToBytesError;
#[rustfmt::skip] // Keep public re-exports separate.
pub use crate::error::{HexToBytesError, OddLengthStringError, InvalidCharError};

/// Iterator over a hex-encoded string slice which decodes hex and yields bytes.
pub struct HexToBytesIter<'a> {
Expand All @@ -33,7 +34,7 @@ impl<'a> HexToBytesIter<'a> {
#[inline]
pub fn new(s: &'a str) -> Result<HexToBytesIter<'a>, HexToBytesError> {
if s.len() % 2 != 0 {
Err(HexToBytesError::OddLengthString(s.len()))
Err(HexToBytesError::OddLengthString(OddLengthStringError { len: s.len() }))
} else {
Ok(HexToBytesIter { iter: s.bytes() })
}
Expand Down Expand Up @@ -93,8 +94,12 @@ impl<'a> io::Read for HexToBytesIter<'a> {

/// `hi` and `lo` are bytes representing hex characters.
fn hex_chars_to_byte(hi: u8, lo: u8) -> Result<u8, HexToBytesError> {
let hih = (hi as char).to_digit(16).ok_or(HexToBytesError::InvalidChar(hi))?;
let loh = (lo as char).to_digit(16).ok_or(HexToBytesError::InvalidChar(lo))?;
let hih = (hi as char)
.to_digit(16)
.ok_or(HexToBytesError::InvalidChar(InvalidCharError { invalid: hi }))?;
let loh = (lo as char)
.to_digit(16)
.ok_or(HexToBytesError::InvalidChar(InvalidCharError { invalid: lo }))?;

let ret = (hih << 4) + loh;
Ok(ret as u8)
Expand Down
34 changes: 23 additions & 11 deletions src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::alloc::vec::Vec;
use crate::iter::HexToBytesIter;

#[rustfmt::skip] // Keep public re-exports separate.
pub use crate::error::{HexToBytesError, HexToArrayError};
pub use crate::error::{HexToBytesError, HexToArrayError, InvalidLengthError, OddLengthStringError, InvalidCharError};

/// Trait for objects that can be deserialized from hex strings.
pub trait FromHex: Sized {
Expand Down Expand Up @@ -58,7 +58,10 @@ macro_rules! impl_fromhex_array {
}
Ok(ret)
} else {
Err(HexToArrayError::InvalidLength(2 * $len, 2 * iter.len()))
Err(HexToArrayError::InvalidLength(InvalidLengthError {
expected: 2 * $len,
got: 2 * iter.len(),
}))
}
}
}
Expand Down Expand Up @@ -93,21 +96,28 @@ mod tests {
#[test]
#[cfg(feature = "alloc")]
fn hex_error() {
use HexToBytesError::*;

let oddlen = "0123456789abcdef0";
let badchar1 = "Z123456789abcdef";
let badchar2 = "012Y456789abcdeb";
let badchar3 = "«23456789abcdef";

assert_eq!(Vec::<u8>::from_hex(oddlen), Err(OddLengthString(17)));
assert_eq!(Vec::<u8>::from_hex(oddlen), Err(OddLengthStringError { len: 17 }.into()));
assert_eq!(
<[u8; 4]>::from_hex(oddlen),
Err(HexToArrayError::Conversion(OddLengthString(17)))
Err(HexToBytesError::OddLengthString(OddLengthStringError { len: 17 }).into())
);
assert_eq!(
Vec::<u8>::from_hex(badchar1),
Err(HexToBytesError::InvalidChar(InvalidCharError { invalid: b'Z' }).into())
);
assert_eq!(
Vec::<u8>::from_hex(badchar2),
Err(HexToBytesError::InvalidChar(InvalidCharError { invalid: b'Y' }).into())
);
assert_eq!(
Vec::<u8>::from_hex(badchar3),
Err(HexToBytesError::InvalidChar(InvalidCharError { invalid: 194 }).into())
);
assert_eq!(Vec::<u8>::from_hex(badchar1), Err(InvalidChar(b'Z')));
assert_eq!(Vec::<u8>::from_hex(badchar2), Err(InvalidChar(b'Y')));
assert_eq!(Vec::<u8>::from_hex(badchar3), Err(InvalidChar(194)));
}

#[test]
Expand All @@ -117,9 +127,11 @@ mod tests {
}
#[test]
fn hex_to_array_error() {
use HexToArrayError::*;
let len_sixteen = "0123456789abcdef";
assert_eq!(<[u8; 4]>::from_hex(len_sixteen), Err(InvalidLength(8, 16)));
assert_eq!(
<[u8; 4]>::from_hex(len_sixteen),
Err(InvalidLengthError { expected: 8, got: 16 }.into())
)
}

#[test]
Expand Down

0 comments on commit dee6b08

Please sign in to comment.