Hide base58::Error internals

As is convention here in `rust-bitcoin`, hide the `base58::Error`
internals by adding struct error types.
This commit is contained in:
Tobin C. Harding 2024-02-21 16:53:05 +11:00
parent 4f68e79da0
commit af49841433
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
2 changed files with 74 additions and 15 deletions

View File

@ -6,16 +6,16 @@ use core::fmt;
use internals::write_err;
/// An error that might occur during base58 decoding.
/// An error occurred during base58 decoding (with checksum).
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
/// Invalid character while decoding.
Decode(InvalidCharacterError),
/// Checksum was not correct (expected, actual).
BadChecksum(u32, u32),
/// Checked data was less than 4 bytes.
TooShort(usize),
/// Checksum was not correct.
IncorrectChecksum(IncorrectChecksumError),
/// Checked data was too short.
TooShort(TooShortError),
}
internals::impl_from_infallible!(Error);
@ -26,9 +26,8 @@ impl fmt::Display for Error {
match *self {
Decode(ref e) => write_err!(f, "decode"; e),
BadChecksum(exp, actual) =>
write!(f, "base58ck checksum {:#x} does not match expected {:#x}", actual, exp),
TooShort(_) => write!(f, "base58ck data not even long enough for a checksum"),
IncorrectChecksum(ref e) => write_err!(f, "incorrect checksum"; e),
TooShort(ref e) => write_err!(f, "too short"; e),
}
}
}
@ -38,10 +37,10 @@ impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use Error::*;
match self {
match *self {
Decode(ref e) => Some(e),
BadChecksum(_, _)
| TooShort(_) => None,
IncorrectChecksum(ref e) => Some(e),
TooShort(ref e) => Some(e),
}
}
}
@ -51,6 +50,64 @@ impl From<InvalidCharacterError> for Error {
fn from(e: InvalidCharacterError) -> Self { Self::Decode(e) }
}
impl From<IncorrectChecksumError> for Error {
#[inline]
fn from(e: IncorrectChecksumError) -> Self { Self::IncorrectChecksum(e) }
}
impl From<TooShortError> for Error {
#[inline]
fn from(e: TooShortError) -> Self { Self::TooShort(e) }
}
/// Checksum was not correct.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IncorrectChecksumError {
/// The incorrect checksum.
pub(super) incorrect: u32,
/// The expected checksum.
pub(super) expected: u32,
}
impl IncorrectChecksumError {
/// Returns the incorrect checksum along with the expected checksum.
pub fn incorrect_checksum(&self) -> (u32, u32) { (self.incorrect, self.expected) }
}
impl fmt::Display for IncorrectChecksumError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"base58 checksum {:#x} does not match expected {:#x}",
self.incorrect, self.expected
)
}
}
#[cfg(feature = "std")]
impl std::error::Error for IncorrectChecksumError {}
/// The decode base58 data was too short (require at least 4 bytes for checksum).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TooShortError {
/// The length of the decoded data.
pub(super) length: usize,
}
impl TooShortError {
/// Returns the invalid base58 string length (require at least 4 bytes for checksum).
pub fn invalid_base58_length(&self) -> usize { self.length }
}
impl fmt::Display for TooShortError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "base58 decoded data was not long enough, must be at least 4 byte: {}", self.length)
}
}
#[cfg(feature = "std")]
impl std::error::Error for TooShortError {}
/// Found a invalid ASCII byte while decoding base58 string.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidCharacterError {

View File

@ -34,6 +34,8 @@ pub use std::{string::String, vec::Vec};
use hashes::{sha256d, Hash};
use crate::error::{IncorrectChecksumError, TooShortError};
#[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)]
pub use self::error::{Error, InvalidCharacterError};
@ -93,7 +95,7 @@ pub fn decode(data: &str) -> Result<Vec<u8>, InvalidCharacterError> {
pub fn decode_check(data: &str) -> Result<Vec<u8>, Error> {
let mut ret: Vec<u8> = decode(data)?;
if ret.len() < 4 {
return Err(Error::TooShort(ret.len()));
return Err(TooShortError { length: ret.len() }.into());
}
let check_start = ret.len() - 4;
@ -104,8 +106,8 @@ pub fn decode_check(data: &str) -> Result<Vec<u8>, Error> {
let expected = u32::from_le_bytes(hash_check);
let actual = u32::from_le_bytes(data_check);
if expected != actual {
return Err(Error::BadChecksum(expected, actual));
if actual != expected {
return Err(IncorrectChecksumError { incorrect: actual, expected }.into());
}
ret.truncate(check_start);
@ -282,6 +284,6 @@ mod tests {
// Check that empty slice passes roundtrip.
assert_eq!(decode_check(&encode_check(&[])), Ok(vec![]));
// Check that `len > 4` is enforced.
assert_eq!(decode_check(&encode(&[1, 2, 3])), Err(Error::TooShort(3)));
assert_eq!(decode_check(&encode(&[1, 2, 3])), Err(TooShortError { length: 3 }.into()));
}
}