Skip to content

Commit 78df275

Browse files
committed
Merge #44: Hide all error internals
af04208 Hide all errors (Tobin C. Harding) 881c098 Move error code to error module (Tobin C. Harding) Pull request description: Before we do v1.0 we want to hide all the error internals so that we can modify/improve them without breaking the public API. Please pay particular attention to the `Display` strings because we they are considered part of the public API so we don't want to change them willy-nilly. Of note, because the enums have inner structs it was difficult to not duplicate the error message. Resolve: #42 ACKs for top commit: Kixunil: ACK af04208 apoelstra: ACK af04208 Tree-SHA512: 3aa197c7d12d5081055e99bafd15ecb0d9ad3f374c7d32e9a98d3363cd0e455027f32d9e756668cbbba499a0f4028fcfe1ded1a6957105858ec7a1edc0325d22
2 parents 9c6df3e + af04208 commit 78df275

File tree

4 files changed

+169
-84
lines changed

4 files changed

+169
-84
lines changed

examples/custom.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ impl<'a> fmt::UpperHex for DisplayALittleBitHexy<'a> {
117117
}
118118

119119
/// Example Error.
120-
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
120+
#[derive(Debug, Clone, PartialEq, Eq)]
121121
pub enum Error {
122122
/// Conversion error while parsing hex string.
123123
Conversion(HexToBytesError),

src/error.rs

+146
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
// SPDX-License-Identifier: CC0-1.0
22

3+
use core::fmt;
4+
5+
use crate::write_err;
6+
37
/// Formats error.
48
///
59
/// If `std` feature is OFF appends error source (delimited by `: `). We do this because
@@ -21,3 +25,145 @@ macro_rules! write_err {
2125
}
2226
}
2327
}
28+
29+
/// Hex decoding error.
30+
#[derive(Debug, Clone, PartialEq, Eq)]
31+
pub enum HexToBytesError {
32+
/// Non-hexadecimal character.
33+
InvalidChar(InvalidCharError),
34+
/// Purported hex string had odd length.
35+
OddLengthString(OddLengthStringError),
36+
}
37+
38+
impl fmt::Display for HexToBytesError {
39+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40+
use HexToBytesError::*;
41+
42+
match *self {
43+
InvalidChar(ref e) => write_err!(f, "invalid char, failed to create bytes from hex"; e),
44+
OddLengthString(ref e) =>
45+
write_err!(f, "odd length, failed to create bytes from hex"; e),
46+
}
47+
}
48+
}
49+
50+
#[cfg(feature = "std")]
51+
impl std::error::Error for HexToBytesError {
52+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
53+
use HexToBytesError::*;
54+
55+
match *self {
56+
InvalidChar(ref e) => Some(e),
57+
OddLengthString(ref e) => Some(e),
58+
}
59+
}
60+
}
61+
62+
impl From<InvalidCharError> for HexToBytesError {
63+
#[inline]
64+
fn from(e: InvalidCharError) -> Self { Self::InvalidChar(e) }
65+
}
66+
67+
impl From<OddLengthStringError> for HexToBytesError {
68+
#[inline]
69+
fn from(e: OddLengthStringError) -> Self { Self::OddLengthString(e) }
70+
}
71+
72+
/// Invalid hex character.
73+
#[derive(Debug, Clone, PartialEq, Eq)]
74+
#[non_exhaustive]
75+
pub struct InvalidCharError {
76+
pub(crate) invalid: u8,
77+
}
78+
79+
impl fmt::Display for InvalidCharError {
80+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81+
write!(f, "invalid hex char {}", self.invalid)
82+
}
83+
}
84+
85+
#[cfg(feature = "std")]
86+
impl std::error::Error for InvalidCharError {
87+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
88+
}
89+
90+
/// Purported hex string had odd length.
91+
#[derive(Debug, Clone, PartialEq, Eq)]
92+
#[non_exhaustive]
93+
pub struct OddLengthStringError {
94+
pub(crate) len: usize,
95+
}
96+
97+
impl fmt::Display for OddLengthStringError {
98+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99+
write!(f, "odd hex string length {}", self.len)
100+
}
101+
}
102+
103+
#[cfg(feature = "std")]
104+
impl std::error::Error for OddLengthStringError {
105+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
106+
}
107+
108+
/// Hex decoding error.
109+
#[derive(Debug, Clone, PartialEq, Eq)]
110+
pub enum HexToArrayError {
111+
/// Conversion error while parsing hex string.
112+
Conversion(HexToBytesError),
113+
/// Tried to parse fixed-length hash from a string with the wrong length (got, want).
114+
InvalidLength(InvalidLengthError),
115+
}
116+
117+
impl fmt::Display for HexToArrayError {
118+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119+
use HexToArrayError::*;
120+
121+
match *self {
122+
Conversion(ref e) =>
123+
crate::write_err!(f, "conversion error, failed to create array from hex"; e),
124+
InvalidLength(ref e) =>
125+
write_err!(f, "invalid length, failed to create array from hex"; e),
126+
}
127+
}
128+
}
129+
130+
#[cfg(feature = "std")]
131+
impl std::error::Error for HexToArrayError {
132+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
133+
use HexToArrayError::*;
134+
135+
match *self {
136+
Conversion(ref e) => Some(e),
137+
InvalidLength(ref e) => Some(e),
138+
}
139+
}
140+
}
141+
142+
impl From<HexToBytesError> for HexToArrayError {
143+
#[inline]
144+
fn from(e: HexToBytesError) -> Self { Self::Conversion(e) }
145+
}
146+
147+
impl From<InvalidLengthError> for HexToArrayError {
148+
#[inline]
149+
fn from(e: InvalidLengthError) -> Self { Self::InvalidLength(e) }
150+
}
151+
152+
/// Tried to parse fixed-length hash from a string with the wrong length.
153+
#[derive(Debug, Clone, PartialEq, Eq)]
154+
#[non_exhaustive]
155+
pub struct InvalidLengthError {
156+
pub(crate) expected: usize,
157+
pub(crate) got: usize,
158+
}
159+
160+
impl fmt::Display for InvalidLengthError {
161+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
162+
write!(f, "bad hex string length {} (expected {})", self.got, self.expected)
163+
}
164+
}
165+
166+
#[cfg(feature = "std")]
167+
impl std::error::Error for InvalidLengthError {
168+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
169+
}

src/iter.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ use std::io;
1010
#[cfg(all(feature = "core2", not(feature = "std")))]
1111
use core2::io;
1212

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

1518
/// Iterator over a hex-encoded string slice which decodes hex and yields bytes.
1619
pub struct HexToBytesIter<'a> {
@@ -33,7 +36,7 @@ impl<'a> HexToBytesIter<'a> {
3336
#[inline]
3437
pub fn new(s: &'a str) -> Result<HexToBytesIter<'a>, HexToBytesError> {
3538
if s.len() % 2 != 0 {
36-
Err(HexToBytesError::OddLengthString(s.len()))
39+
Err(OddLengthStringError { len: s.len() }.into())
3740
} else {
3841
Ok(HexToBytesIter { iter: s.bytes() })
3942
}
@@ -93,8 +96,8 @@ impl<'a> io::Read for HexToBytesIter<'a> {
9396

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

99102
let ret = (hih << 4) + loh;
100103
Ok(ret as u8)

src/parse.rs

+15-79
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ use core::{fmt, str};
66

77
#[cfg(all(feature = "alloc", not(feature = "std")))]
88
use crate::alloc::vec::Vec;
9+
use crate::error::InvalidLengthError;
910
use crate::iter::HexToBytesIter;
1011

12+
#[rustfmt::skip] // Keep public re-exports separate.
13+
pub use crate::error::{HexToBytesError, HexToArrayError};
14+
1115
/// Trait for objects that can be deserialized from hex strings.
1216
pub trait FromHex: Sized {
1317
/// Error type returned while parsing hex string.
@@ -37,37 +41,6 @@ impl FromHex for Vec<u8> {
3741
}
3842
}
3943

40-
/// Hex decoding error.
41-
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
42-
pub enum HexToBytesError {
43-
/// Non-hexadecimal character.
44-
InvalidChar(u8),
45-
/// Purported hex string had odd length.
46-
OddLengthString(usize),
47-
}
48-
49-
impl fmt::Display for HexToBytesError {
50-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51-
use HexToBytesError::*;
52-
53-
match *self {
54-
InvalidChar(ch) => write!(f, "invalid hex character {}", ch),
55-
OddLengthString(ell) => write!(f, "odd hex string length {}", ell),
56-
}
57-
}
58-
}
59-
60-
#[cfg(feature = "std")]
61-
impl std::error::Error for HexToBytesError {
62-
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
63-
use HexToBytesError::*;
64-
65-
match self {
66-
InvalidChar(_) | OddLengthString(_) => None,
67-
}
68-
}
69-
}
70-
7144
macro_rules! impl_fromhex_array {
7245
($len:expr) => {
7346
impl FromHex for [u8; $len] {
@@ -86,7 +59,7 @@ macro_rules! impl_fromhex_array {
8659
}
8760
Ok(ret)
8861
} else {
89-
Err(HexToArrayError::InvalidLength(2 * $len, 2 * iter.len()))
62+
Err(InvalidLengthError { expected: 2 * $len, got: 2 * iter.len() }.into())
9063
}
9164
}
9265
}
@@ -113,67 +86,28 @@ impl_fromhex_array!(256);
11386
impl_fromhex_array!(384);
11487
impl_fromhex_array!(512);
11588

116-
/// Hex decoding error.
117-
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
118-
pub enum HexToArrayError {
119-
/// Conversion error while parsing hex string.
120-
Conversion(HexToBytesError),
121-
/// Tried to parse fixed-length hash from a string with the wrong length (got, want).
122-
InvalidLength(usize, usize),
123-
}
124-
125-
impl fmt::Display for HexToArrayError {
126-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127-
use HexToArrayError::*;
128-
129-
match *self {
130-
Conversion(ref e) => crate::write_err!(f, "conversion error"; e),
131-
InvalidLength(got, want) =>
132-
write!(f, "bad hex string length {} (expected {})", got, want),
133-
}
134-
}
135-
}
136-
137-
#[cfg(feature = "std")]
138-
impl std::error::Error for HexToArrayError {
139-
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
140-
use HexToArrayError::*;
141-
142-
match *self {
143-
Conversion(ref e) => Some(e),
144-
InvalidLength(_, _) => None,
145-
}
146-
}
147-
}
148-
149-
impl From<HexToBytesError> for HexToArrayError {
150-
#[inline]
151-
fn from(e: HexToBytesError) -> Self { Self::Conversion(e) }
152-
}
153-
15489
#[cfg(test)]
15590
mod tests {
15691
use super::*;
15792
use crate::display::DisplayHex;
93+
use crate::error::{InvalidCharError, InvalidLengthError, OddLengthStringError};
15894

15995
#[test]
16096
#[cfg(feature = "alloc")]
16197
fn hex_error() {
162-
use HexToBytesError::*;
163-
16498
let oddlen = "0123456789abcdef0";
16599
let badchar1 = "Z123456789abcdef";
166100
let badchar2 = "012Y456789abcdeb";
167101
let badchar3 = "«23456789abcdef";
168102

169-
assert_eq!(Vec::<u8>::from_hex(oddlen), Err(OddLengthString(17)));
103+
assert_eq!(Vec::<u8>::from_hex(oddlen), Err(OddLengthStringError { len: 17 }.into()));
170104
assert_eq!(
171105
<[u8; 4]>::from_hex(oddlen),
172-
Err(HexToArrayError::Conversion(OddLengthString(17)))
106+
Err(HexToBytesError::OddLengthString(OddLengthStringError { len: 17 }).into())
173107
);
174-
assert_eq!(Vec::<u8>::from_hex(badchar1), Err(InvalidChar(b'Z')));
175-
assert_eq!(Vec::<u8>::from_hex(badchar2), Err(InvalidChar(b'Y')));
176-
assert_eq!(Vec::<u8>::from_hex(badchar3), Err(InvalidChar(194)));
108+
assert_eq!(Vec::<u8>::from_hex(badchar1), Err(InvalidCharError { invalid: b'Z' }.into()));
109+
assert_eq!(Vec::<u8>::from_hex(badchar2), Err(InvalidCharError { invalid: b'Y' }.into()));
110+
assert_eq!(Vec::<u8>::from_hex(badchar3), Err(InvalidCharError { invalid: 194 }.into()));
177111
}
178112

179113
#[test]
@@ -183,9 +117,11 @@ mod tests {
183117
}
184118
#[test]
185119
fn hex_to_array_error() {
186-
use HexToArrayError::*;
187120
let len_sixteen = "0123456789abcdef";
188-
assert_eq!(<[u8; 4]>::from_hex(len_sixteen), Err(InvalidLength(8, 16)));
121+
assert_eq!(
122+
<[u8; 4]>::from_hex(len_sixteen),
123+
Err(InvalidLengthError { expected: 8, got: 16 }.into())
124+
)
189125
}
190126

191127
#[test]

0 commit comments

Comments
 (0)