Flesh out hex unit parsing API

Add to `units::parse` the complete suit of hex unit parsing functions:

- remove prefix
- assert without prefix
- parse with or without prefix
- parse with prefix
- parse without prefix
- parse prefix unchecked

Refactor `bitcoin` to use the exact function we need, removing code
duplication.

This is a breaking change to `units`, it does however keep the current
re-exports from the public, now empty, `bitcoin::error` module.
This commit is contained in:
Tobin C. Harding 2024-05-08 14:49:43 +10:00
parent 1142d16192
commit a5b93cb159
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
6 changed files with 290 additions and 231 deletions

View File

@ -11,12 +11,11 @@ use core::fmt;
use io::{BufRead, Write}; use io::{BufRead, Write};
#[cfg(all(test, mutate))] #[cfg(all(test, mutate))]
use mutagen::mutate; use mutagen::mutate;
use units::parse; use units::parse::{self, PrefixedHexError, UnprefixedHexError};
#[cfg(doc)] #[cfg(doc)]
use crate::absolute; use crate::absolute;
use crate::consensus::encode::{self, Decodable, Encodable}; use crate::consensus::encode::{self, Decodable, Encodable};
use crate::error::{ContainsPrefixError, MissingPrefixError, PrefixedHexError, UnprefixedHexError};
#[rustfmt::skip] // Keep public re-exports separate. #[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)] #[doc(inline)]
@ -92,26 +91,15 @@ impl LockTime {
/// The number of bytes that the locktime contributes to the size of a transaction. /// The number of bytes that the locktime contributes to the size of a transaction.
pub const SIZE: usize = 4; // Serialized length of a u32. pub const SIZE: usize = 4; // Serialized length of a u32.
/// Creates a `LockTime` from an prefixed hex string. /// Creates a `LockTime` from a prefixed hex string.
pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> { pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
let stripped = if let Some(stripped) = s.strip_prefix("0x") { let lock_time = parse::hex_u32_prefixed(s)?;
stripped
} else if let Some(stripped) = s.strip_prefix("0X") {
stripped
} else {
return Err(MissingPrefixError::new(s).into());
};
let lock_time = parse::hex_u32(stripped)?;
Ok(Self::from_consensus(lock_time)) Ok(Self::from_consensus(lock_time))
} }
/// Creates a `LockTime` from an unprefixed hex string. /// Creates a `LockTime` from an unprefixed hex string.
pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> { pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
if s.starts_with("0x") || s.starts_with("0X") { let lock_time = parse::hex_u32_unprefixed(s)?;
return Err(ContainsPrefixError::new(s).into());
}
let lock_time = parse::hex_u32(s)?;
Ok(Self::from_consensus(lock_time)) Ok(Self::from_consensus(lock_time))
} }

View File

@ -15,7 +15,7 @@ use core::{cmp, fmt, str};
use hashes::{sha256d, Hash}; use hashes::{sha256d, Hash};
use internals::write_err; use internals::write_err;
use io::{BufRead, Write}; use io::{BufRead, Write};
use units::parse; use units::parse::{self, PrefixedHexError, UnprefixedHexError};
use super::Weight; use super::Weight;
use crate::blockdata::locktime::absolute::{self, Height, Time}; use crate::blockdata::locktime::absolute::{self, Height, Time};
@ -24,7 +24,6 @@ use crate::blockdata::script::{Script, ScriptBuf};
use crate::blockdata::witness::Witness; use crate::blockdata::witness::Witness;
use crate::blockdata::FeeRate; use crate::blockdata::FeeRate;
use crate::consensus::{encode, Decodable, Encodable}; use crate::consensus::{encode, Decodable, Encodable};
use crate::error::{ContainsPrefixError, MissingPrefixError, PrefixedHexError, UnprefixedHexError};
use crate::internal_macros::{impl_consensus_encoding, impl_hashencode}; use crate::internal_macros::{impl_consensus_encoding, impl_hashencode};
use crate::prelude::*; use crate::prelude::*;
#[cfg(doc)] #[cfg(doc)]
@ -404,26 +403,15 @@ impl Sequence {
self.is_relative_lock_time() & (self.0 & Sequence::LOCK_TYPE_MASK > 0) self.is_relative_lock_time() & (self.0 & Sequence::LOCK_TYPE_MASK > 0)
} }
/// Creates a `Sequence` from an prefixed hex string. /// Creates a `Sequence` from a prefixed hex string.
pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> { pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
let stripped = if let Some(stripped) = s.strip_prefix("0x") { let lock_time = parse::hex_u32_prefixed(s)?;
stripped Ok(Self::from_consensus(lock_time))
} else if let Some(stripped) = s.strip_prefix("0X") {
stripped
} else {
return Err(MissingPrefixError::new(s).into());
};
let sequence = parse::hex_u32(stripped)?;
Ok(Self::from_consensus(sequence))
} }
/// Creates a `Sequence` from an unprefixed hex string. /// Creates a `Sequence` from an unprefixed hex string.
pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> { pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
if s.starts_with("0x") || s.starts_with("0X") { let lock_time = parse::hex_u32_unprefixed(s)?;
return Err(ContainsPrefixError::new(s).into());
}
let lock_time = parse::hex_u32(s)?;
Ok(Self::from_consensus(lock_time)) Ok(Self::from_consensus(lock_time))
} }

View File

@ -2,131 +2,8 @@
//! Contains error types and other error handling tools. //! Contains error types and other error handling tools.
use core::fmt;
use internals::write_err;
use crate::prelude::*;
#[rustfmt::skip] // Keep public re-exports separate. #[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)] #[doc(inline)]
pub use crate::parse::ParseIntError; pub use units::parse::{
ContainsPrefixError, MissingPrefixError, ParseIntError, PrefixedHexError, UnprefixedHexError,
/// Error returned when parsing integer from an supposedly prefixed hex string for };
/// a type that can be created infallibly from an integer.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum PrefixedHexError {
/// Hex string is missing prefix.
MissingPrefix(MissingPrefixError),
/// Error parsing integer from hex string.
ParseInt(ParseIntError),
}
impl fmt::Display for PrefixedHexError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use PrefixedHexError::*;
match *self {
MissingPrefix(ref e) => write_err!(f, "hex string is missing prefix"; e),
ParseInt(ref e) => write_err!(f, "prefixed hex string invalid int"; e),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for PrefixedHexError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use PrefixedHexError::*;
match *self {
MissingPrefix(ref e) => Some(e),
ParseInt(ref e) => Some(e),
}
}
}
impl From<MissingPrefixError> for PrefixedHexError {
fn from(e: MissingPrefixError) -> Self { Self::MissingPrefix(e) }
}
impl From<ParseIntError> for PrefixedHexError {
fn from(e: ParseIntError) -> Self { Self::ParseInt(e) }
}
/// Error returned when parsing integer from an supposedly un-prefixed hex string.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum UnprefixedHexError {
/// Hex string contains prefix.
ContainsPrefix(ContainsPrefixError),
/// Error parsing integer from string.
ParseInt(ParseIntError),
}
impl fmt::Display for UnprefixedHexError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use UnprefixedHexError::*;
match *self {
ContainsPrefix(ref e) => write_err!(f, "hex string is contains prefix"; e),
ParseInt(ref e) => write_err!(f, "hex string parse int"; e),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for UnprefixedHexError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use UnprefixedHexError::*;
match *self {
ContainsPrefix(ref e) => Some(e),
ParseInt(ref e) => Some(e),
}
}
}
impl From<ContainsPrefixError> for UnprefixedHexError {
fn from(e: ContainsPrefixError) -> Self { Self::ContainsPrefix(e) }
}
impl From<ParseIntError> for UnprefixedHexError {
fn from(e: ParseIntError) -> Self { Self::ParseInt(e) }
}
/// Error when hex string is missing a prefix (e.g. 0x).
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct MissingPrefixError {
hex: String,
}
impl MissingPrefixError {
pub(crate) fn new(s: &str) -> Self { Self { hex: s.into() } }
}
impl fmt::Display for MissingPrefixError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "hex string is missing a prefix (e.g. 0x): {}", self.hex)
}
}
#[cfg(feature = "std")]
impl std::error::Error for MissingPrefixError {}
/// Error when hex string contains a prefix (e.g. 0x).
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ContainsPrefixError {
hex: String,
}
impl ContainsPrefixError {
pub(crate) fn new(s: &str) -> Self { Self { hex: s.into() } }
}
impl fmt::Display for ContainsPrefixError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "hex string contains a prefix: {}", self.hex)
}
}
#[cfg(feature = "std")]
impl std::error::Error for ContainsPrefixError {}

View File

@ -18,9 +18,7 @@ use crate::block::Header;
use crate::blockdata::block::BlockHash; use crate::blockdata::block::BlockHash;
use crate::consensus::encode::{self, Decodable, Encodable}; use crate::consensus::encode::{self, Decodable, Encodable};
use crate::consensus::Params; use crate::consensus::Params;
use crate::error::{ use crate::error::{ParseIntError, PrefixedHexError, UnprefixedHexError};
ContainsPrefixError, MissingPrefixError, ParseIntError, PrefixedHexError, UnprefixedHexError,
};
/// Implement traits and methods shared by `Target` and `Work`. /// Implement traits and methods shared by `Target` and `Work`.
macro_rules! do_impl { macro_rules! do_impl {
@ -350,27 +348,16 @@ do_impl!(Target);
pub struct CompactTarget(u32); pub struct CompactTarget(u32);
impl CompactTarget { impl CompactTarget {
/// Creates a `CompactTarget` from an prefixed hex string. /// Creates a `CompactTarget` from a prefixed hex string.
pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> { pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
let stripped = if let Some(stripped) = s.strip_prefix("0x") { let target = parse::hex_u32_prefixed(s)?;
stripped
} else if let Some(stripped) = s.strip_prefix("0X") {
stripped
} else {
return Err(MissingPrefixError::new(s).into());
};
let target = parse::hex_u32(stripped)?;
Ok(Self::from_consensus(target)) Ok(Self::from_consensus(target))
} }
/// Creates a `CompactTarget` from an unprefixed hex string. /// Creates a `CompactTarget` from an unprefixed hex string.
pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> { pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
if s.starts_with("0x") || s.starts_with("0X") { let target = parse::hex_u32_unprefixed(s)?;
return Err(ContainsPrefixError::new(s).into()); Ok(Self::from_consensus(target))
}
let lock_time = parse::hex_u32(s)?;
Ok(Self::from_consensus(lock_time))
} }
/// Computes the [`CompactTarget`] from a difficulty adjustment. /// Computes the [`CompactTarget`] from a difficulty adjustment.
@ -499,36 +486,28 @@ impl U256 {
/// Creates a `U256` from a prefixed hex string. /// Creates a `U256` from a prefixed hex string.
fn from_hex(s: &str) -> Result<Self, PrefixedHexError> { fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
let stripped = if let Some(stripped) = s.strip_prefix("0x") { let checked = parse::hex_remove_prefix(s)?;
stripped Ok(U256::from_hex_internal(checked)?)
} else if let Some(stripped) = s.strip_prefix("0X") {
stripped
} else {
return Err(MissingPrefixError::new(s).into());
};
Ok(U256::from_hex_internal(stripped)?)
} }
/// Creates a `U256` from an unprefixed hex string. /// Creates a `U256` from an unprefixed hex string.
fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> { fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
if s.starts_with("0x") || s.starts_with("0X") { let checked = parse::hex_check_unprefixed(s)?;
return Err(ContainsPrefixError::new(s).into()); Ok(U256::from_hex_internal(checked)?)
}
Ok(U256::from_hex_internal(s)?)
} }
// Caller to ensure `s` does not contain a prefix. // Caller to ensure `s` does not contain a prefix.
fn from_hex_internal(s: &str) -> Result<Self, ParseIntError> { fn from_hex_internal(s: &str) -> Result<Self, ParseIntError> {
let (high, low) = if s.len() < 32 { let (high, low) = if s.len() < 32 {
let low = parse::hex_u128(s)?; let low = parse::hex_u128_unchecked(s)?;
(0, low) (0, low)
} else { } else {
let high_len = s.len() - 32; let high_len = s.len() - 32;
let high_s = &s[..high_len]; let high_s = &s[..high_len];
let low_s = &s[high_len..]; let low_s = &s[high_len..];
let high = parse::hex_u128(high_s)?; let high = parse::hex_u128_unchecked(high_s)?;
let low = parse::hex_u128(low_s)?; let low = parse::hex_u128_unchecked(low_s)?;
(high, low) (high, low)
}; };

View File

@ -6,7 +6,9 @@ use core::fmt;
use internals::write_err; use internals::write_err;
use crate::parse::{self, ParseIntError}; use crate::parse::ParseIntError;
#[cfg(feature = "alloc")]
use crate::parse;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use crate::prelude::*; use crate::prelude::*;
@ -233,7 +235,7 @@ where
S: AsRef<str> + Into<String>, S: AsRef<str> + Into<String>,
F: FnOnce(u32) -> Result<T, ConversionError>, F: FnOnce(u32) -> Result<T, ConversionError>,
{ {
let n = i64::from_str_radix(parse::strip_hex_prefix(s.as_ref()), 16) let n = i64::from_str_radix(parse::hex_remove_optional_prefix(s.as_ref()), 16)
.map_err(ParseError::invalid_int(s))?; .map_err(ParseError::invalid_int(s))?;
let n = u32::try_from(n).map_err(|_| ParseError::Conversion(n))?; let n = u32::try_from(n).map_err(|_| ParseError::Conversion(n))?;
f(n).map_err(ParseError::from).map_err(Into::into) f(n).map_err(ParseError::from).map_err(Into::into)

View File

@ -88,43 +88,6 @@ pub fn int<T: Integer, S: AsRef<str> + Into<String>>(s: S) -> Result<T, ParseInt
}) })
} }
/// Parses a `u32` from a hex string.
///
/// Input string may or may not contain a `0x` prefix.
pub fn hex_u32<S: AsRef<str> + Into<String>>(s: S) -> Result<u32, ParseIntError> {
let stripped = strip_hex_prefix(s.as_ref());
u32::from_str_radix(stripped, 16).map_err(|error| ParseIntError {
input: s.into(),
bits: 32,
is_signed: false,
source: error,
})
}
/// Parses a `u128` from a hex string.
///
/// Input string may or may not contain a `0x` prefix.
pub fn hex_u128<S: AsRef<str> + Into<String>>(s: S) -> Result<u128, ParseIntError> {
let stripped = strip_hex_prefix(s.as_ref());
u128::from_str_radix(stripped, 16).map_err(|error| ParseIntError {
input: s.into(),
bits: 128,
is_signed: false,
source: error,
})
}
/// Strips the hex prefix off `s` if one is present.
pub(crate) fn strip_hex_prefix(s: &str) -> &str {
if let Some(stripped) = s.strip_prefix("0x") {
stripped
} else if let Some(stripped) = s.strip_prefix("0X") {
stripped
} else {
s
}
}
/// Implements `TryFrom<$from> for $to` using `parse::int`, mapping the output using infallible /// Implements `TryFrom<$from> for $to` using `parse::int`, mapping the output using infallible
/// conversion function `fn`. /// conversion function `fn`.
#[macro_export] #[macro_export]
@ -197,6 +160,263 @@ macro_rules! impl_parse_str {
} }
} }
/// Removes the prefix `0x` (or `0X`) from hex string.
///
/// # Errors
///
/// If input string does not contain a prefix.
pub fn hex_remove_prefix(s: &str) -> Result<&str, PrefixedHexError> {
if let Some(checked) = s.strip_prefix("0x") {
Ok(checked)
} else if let Some(checked) = s.strip_prefix("0X") {
Ok(checked)
} else {
Err(MissingPrefixError::new(s.into()).into())
}
}
/// Checks hex string does not have a prefix `0x` (or `0X`).
///
/// # Errors
///
/// If input string contains a prefix.
pub fn hex_check_unprefixed(s: &str) -> Result<&str, UnprefixedHexError> {
if s.starts_with("0x") || s.starts_with("0X") {
return Err(ContainsPrefixError::new(s.into()).into());
}
Ok(s)
}
/// Parses a `u32` from a hex string.
///
/// Input string may or may not contain a `0x` (or `0X`) prefix.
///
/// # Errors
///
/// If the input string is not a valid hex encoding of a `u32`.
pub fn hex_u32(s: &str) -> Result<u32, ParseIntError> {
let unchecked = hex_remove_optional_prefix(s);
Ok(hex_u32_unchecked(unchecked)?)
}
/// Parses a `u32` from a prefixed hex string.
///
/// # Errors
///
/// - If input string does not contain a `0x` (or `0X`) prefix.
/// - If input string is not a valid hex encoding of a `u32`.
pub fn hex_u32_prefixed(s: &str) -> Result<u32, PrefixedHexError> {
let checked = hex_remove_prefix(s)?;
Ok(hex_u32_unchecked(checked)?)
}
/// Parses a `u32` from an unprefixed hex string.
///
/// # Errors
///
/// - If input string contains a `0x` (or `0X`) prefix.
/// - If input string is not a valid hex encoding of a `u32`.
pub fn hex_u32_unprefixed(s: &str) -> Result<u32, UnprefixedHexError> {
let checked = hex_check_unprefixed(s)?;
Ok(hex_u32_unchecked(checked)?)
}
/// Parses a `u32` from a hex string.
///
/// # Errors
///
/// - If input string is not a valid hex encoding of a `u32`.
/// - With `InvalidDigit` due to the `x` if there is a prefix.
pub fn hex_u32_unchecked(s: &str) -> Result<u32, ParseIntError> {
u32::from_str_radix(s, 16).map_err(|error| ParseIntError {
input: s.into(),
bits: 32,
is_signed: false,
source: error,
})
}
/// Parses a `u128` from a hex string.
///
/// Input string may or may not contain a `0x` (or `0X`) prefix.
///
/// # Errors
///
/// If the input string is not a valid hex encoding of a `u128`.
pub fn hex_u128(s: &str) -> Result<u128, ParseIntError> {
let unchecked = hex_remove_optional_prefix(s);
Ok(hex_u128_unchecked(unchecked)?)
}
/// Parses a `u128` from a hex string.
///
/// # Errors
///
/// - If input string does not contain a `0x` (or `0X`) prefix.
/// - If input string is not a valid hex encoding of a `u128`.
pub fn hex_u128_prefixed(s: &str) -> Result<u128, PrefixedHexError> {
let checked = hex_remove_prefix(s)?;
Ok(hex_u128_unchecked(checked)?)
}
/// Parses a `u128` from a hex string.
///
/// # Errors
///
/// - If input string contains a `0x` (or `0X`) prefix.
/// - If input string is not a valid hex encoding of a `u128`.
pub fn hex_u128_unprefixed(s: &str) -> Result<u128, UnprefixedHexError> {
let checked = hex_check_unprefixed(s)?;
Ok(hex_u128_unchecked(checked)?)
}
/// Parses a `u128` from a hex string.
///
/// # Errors
///
/// - If input string is not a valid hex encoding of a `u128`.
/// - With `InvalidDigit` due to the `x` if there is a prefix.
pub fn hex_u128_unchecked(s: &str) -> Result<u128, ParseIntError> {
u128::from_str_radix(s, 16).map_err(|error| ParseIntError {
input: s.into(),
bits: 128,
is_signed: false,
source: error,
})
}
/// Strips the hex prefix off `s` if one is present.
pub(crate) fn hex_remove_optional_prefix(s: &str) -> &str {
if let Some(stripped) = s.strip_prefix("0x") {
stripped
} else if let Some(stripped) = s.strip_prefix("0X") {
stripped
} else {
s
}
}
/// Error returned when parsing integer from an supposedly prefixed hex string for
/// a type that can be created infallibly from an integer.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum PrefixedHexError {
/// Hex string is missing prefix.
MissingPrefix(MissingPrefixError),
/// Error parsing integer from hex string.
ParseInt(ParseIntError),
}
impl fmt::Display for PrefixedHexError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use PrefixedHexError::*;
match *self {
MissingPrefix(ref e) => write_err!(f, "hex string is missing prefix"; e),
ParseInt(ref e) => write_err!(f, "prefixed hex string invalid int"; e),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for PrefixedHexError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use PrefixedHexError::*;
match *self {
MissingPrefix(ref e) => Some(e),
ParseInt(ref e) => Some(e),
}
}
}
impl From<MissingPrefixError> for PrefixedHexError {
fn from(e: MissingPrefixError) -> Self { Self::MissingPrefix(e) }
}
impl From<ParseIntError> for PrefixedHexError {
fn from(e: ParseIntError) -> Self { Self::ParseInt(e) }
}
/// Error returned when parsing integer from an supposedly un-prefixed hex string.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum UnprefixedHexError {
/// Hex string contains prefix.
ContainsPrefix(ContainsPrefixError),
/// Error parsing integer from string.
ParseInt(ParseIntError),
}
impl fmt::Display for UnprefixedHexError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use UnprefixedHexError::*;
match *self {
ContainsPrefix(ref e) => write_err!(f, "hex string is contains prefix"; e),
ParseInt(ref e) => write_err!(f, "hex string parse int"; e),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for UnprefixedHexError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use UnprefixedHexError::*;
match *self {
ContainsPrefix(ref e) => Some(e),
ParseInt(ref e) => Some(e),
}
}
}
impl From<ContainsPrefixError> for UnprefixedHexError {
fn from(e: ContainsPrefixError) -> Self { Self::ContainsPrefix(e) }
}
impl From<ParseIntError> for UnprefixedHexError {
fn from(e: ParseIntError) -> Self { Self::ParseInt(e) }
}
/// Error when hex string is missing a prefix (e.g. 0x).
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct MissingPrefixError {
hex: String,
}
impl MissingPrefixError {
/// Creates an error from the string with the missing prefix.
pub(crate) fn new(hex: String) -> Self { Self { hex } }
}
impl fmt::Display for MissingPrefixError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "hex string is missing a prefix (e.g. 0x): {}", self.hex)
}
}
#[cfg(feature = "std")]
impl std::error::Error for MissingPrefixError {}
/// Error when hex string contains a prefix (e.g. 0x).
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ContainsPrefixError {
hex: String,
}
impl ContainsPrefixError {
/// Creates an error from the string that contains the prefix.
pub(crate) fn new(hex: String) -> Self { Self { hex } }
}
impl fmt::Display for ContainsPrefixError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "hex string contains a prefix: {}", self.hex)
}
}
#[cfg(feature = "std")]
impl std::error::Error for ContainsPrefixError {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -228,4 +448,9 @@ mod tests {
let got = hex_u128("deadbeef").expect("failed to parse non-prefixed hex"); let got = hex_u128("deadbeef").expect("failed to parse non-prefixed hex");
assert_eq!(got, want); assert_eq!(got, want);
} }
#[test]
fn parse_u32_from_hex_unchecked_errors_on_prefix() {
assert!(hex_u32_unchecked("0xab").is_err());
}
} }