Merge rust-bitcoin/rust-bitcoin#2492: Remove the FromHexStr trait
b873a3cd44Do infallible int from hex conversions (Tobin C. Harding)4d762cb08cRemove the FromHexStr trait (Tobin C. Harding)026537807fRemove mention of packed (Tobin C. Harding) Pull request description: The `FromHexStr` trait is used to parse integer-like types, however we can achieve the same using inherent methods. Move the hex parsing functionality to inherent methods, keeping the same behaviour in regard to the `0x` prefix. Patch 1 is trivial preparatory cleanup. ACKs for top commit: apoelstra: ACKb873a3cd44sanket1729: ACKb873a3cd44Tree-SHA512: a280169b68304fcc1a531cc9ffb6914b70238efc4c2241a766105053911a373a0334b73e5ea3525c331ccb81ce98c43fea96dae77668804e608376a48d5ed8ac
This commit is contained in:
commit
ea6aa99ae4
|
|
@ -17,10 +17,9 @@ use mutagen::mutate;
|
|||
#[cfg(doc)]
|
||||
use crate::absolute;
|
||||
use crate::consensus::encode::{self, Decodable, Encodable};
|
||||
use crate::error::ParseIntError;
|
||||
use crate::parse::{impl_parse_str, impl_parse_str_from_int_infallible};
|
||||
use crate::error::{ParseIntError, PrefixedHexError, UnprefixedHexError, ContainsPrefixError, MissingPrefixError};
|
||||
use crate::parse::{self, impl_parse_str, impl_parse_str_from_int_infallible};
|
||||
use crate::prelude::*;
|
||||
use crate::string::FromHexStr;
|
||||
|
||||
/// The Threshold for deciding whether a lock time value is a height or a time (see [Bitcoin Core]).
|
||||
///
|
||||
|
|
@ -102,6 +101,29 @@ impl LockTime {
|
|||
/// The number of bytes that the locktime contributes to the size of a transaction.
|
||||
pub const SIZE: usize = 4; // Serialized length of a u32.
|
||||
|
||||
/// Creates a `LockTime` from an prefixed hex string.
|
||||
pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
|
||||
let stripped = if let Some(stripped) = s.strip_prefix("0x") {
|
||||
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))
|
||||
}
|
||||
|
||||
/// Creates a `LockTime` from an unprefixed hex string.
|
||||
pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
|
||||
if s.starts_with("0x") || s.starts_with("0X") {
|
||||
return Err(ContainsPrefixError::new(s).into());
|
||||
}
|
||||
let lock_time = parse::hex_u32(s)?;
|
||||
Ok(Self::from_consensus(lock_time))
|
||||
}
|
||||
|
||||
/// Constructs a `LockTime` from an nLockTime value or the argument to OP_CHEKCLOCKTIMEVERIFY.
|
||||
///
|
||||
/// # Examples
|
||||
|
|
@ -325,16 +347,6 @@ impl fmt::Display for LockTime {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromHexStr for LockTime {
|
||||
type Error = ParseIntError;
|
||||
|
||||
#[inline]
|
||||
fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Self::Error> {
|
||||
let packed_lock_time = crate::parse::hex_u32(s)?;
|
||||
Ok(Self::from_consensus(packed_lock_time))
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for LockTime {
|
||||
#[inline]
|
||||
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
||||
|
|
@ -419,6 +431,11 @@ impl Height {
|
|||
/// The maximum absolute block height.
|
||||
pub const MAX: Self = Height(LOCK_TIME_THRESHOLD - 1);
|
||||
|
||||
/// Creates a `Height` from a hex string.
|
||||
///
|
||||
/// The input string is may or may not contain a typical hex prefix e.g., `0x`.
|
||||
pub fn from_hex(s: &str) -> Result<Self, ParseHeightError> { parse_hex(s, Self::from_consensus) }
|
||||
|
||||
/// Constructs a new block height.
|
||||
///
|
||||
/// # Errors
|
||||
|
|
@ -460,15 +477,6 @@ impl fmt::Display for Height {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
|
||||
}
|
||||
|
||||
impl FromHexStr for Height {
|
||||
type Error = ParseHeightError;
|
||||
|
||||
#[inline]
|
||||
fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Self::Error> {
|
||||
parse_hex(s, Self::from_consensus)
|
||||
}
|
||||
}
|
||||
|
||||
impl_parse_str!(Height, ParseHeightError, parser(Height::from_consensus));
|
||||
|
||||
/// Error returned when parsing block height fails.
|
||||
|
|
@ -508,6 +516,11 @@ impl Time {
|
|||
/// The maximum absolute block time (Sun Feb 07 2106 06:28:15 GMT+0000).
|
||||
pub const MAX: Self = Time(u32::max_value());
|
||||
|
||||
/// Creates a `Time` from a hex string.
|
||||
///
|
||||
/// The input string is may or may not contain a typical hex prefix e.g., `0x`.
|
||||
pub fn from_hex(s: &str) -> Result<Self, ParseTimeError> { parse_hex(s, Self::from_consensus) }
|
||||
|
||||
/// Constructs a new block time.
|
||||
///
|
||||
/// # Errors
|
||||
|
|
@ -549,15 +562,6 @@ impl fmt::Display for Time {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
|
||||
}
|
||||
|
||||
impl FromHexStr for Time {
|
||||
type Error = ParseTimeError;
|
||||
|
||||
#[inline]
|
||||
fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Self::Error> {
|
||||
parse_hex(s, Self::from_consensus)
|
||||
}
|
||||
}
|
||||
|
||||
impl_parse_str!(Time, ParseTimeError, parser(Time::from_consensus));
|
||||
|
||||
/// Error returned when parsing block time fails.
|
||||
|
|
@ -599,7 +603,7 @@ where
|
|||
S: AsRef<str> + Into<String>,
|
||||
F: FnOnce(u32) -> Result<T, ConversionError>,
|
||||
{
|
||||
let n = i64::from_str_radix(s.as_ref(), 16).map_err(ParseError::invalid_int(s))?;
|
||||
let n = i64::from_str_radix(parse::strip_hex_prefix(s.as_ref()), 16).map_err(ParseError::invalid_int(s))?;
|
||||
let n = u32::try_from(n).map_err(|_| ParseError::Conversion(n))?;
|
||||
f(n).map_err(ParseError::from).map_err(Into::into)
|
||||
}
|
||||
|
|
@ -819,63 +823,74 @@ mod tests {
|
|||
assert_eq!(got, "block-height 741521");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lock_time_from_hex_lower() {
|
||||
let lock = LockTime::from_hex("0x6289c350").unwrap();
|
||||
assert_eq!(lock, LockTime::from_consensus(0x6289C350));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lock_time_from_hex_upper() {
|
||||
let lock = LockTime::from_hex("0X6289C350").unwrap();
|
||||
assert_eq!(lock, LockTime::from_consensus(0x6289C350));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lock_time_from_unprefixed_hex_lower() {
|
||||
let lock = LockTime::from_unprefixed_hex("6289c350").unwrap();
|
||||
assert_eq!(lock, LockTime::from_consensus(0x6289C350));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lock_time_from_unprefixed_hex_upper() {
|
||||
let lock = LockTime::from_unprefixed_hex("6289C350").unwrap();
|
||||
assert_eq!(lock, LockTime::from_consensus(0x6289C350));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lock_time_from_invalid_hex_should_err() {
|
||||
let hex = "0xzb93";
|
||||
let result = LockTime::from_hex(hex);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn time_from_str_hex_happy_path() {
|
||||
let actual = Time::from_hex_str("0x6289C350").unwrap();
|
||||
let actual = Time::from_hex("0x6289C350").unwrap();
|
||||
let expected = Time::from_consensus(0x6289C350).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn time_from_str_hex_no_prefix_happy_path() {
|
||||
let time = Time::from_hex_str_no_prefix("6289C350").unwrap();
|
||||
let time = Time::from_hex("6289C350").unwrap();
|
||||
assert_eq!(time, Time(0x6289C350));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn time_from_str_hex_invalid_hex_should_err() {
|
||||
let hex = "0xzb93";
|
||||
let result = Time::from_hex_str(hex);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packed_lock_time_from_str_hex_happy_path() {
|
||||
let actual = LockTime::from_hex_str("0xBA70D").unwrap();
|
||||
let expected = LockTime::from_consensus(0xBA70D);
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packed_lock_time_from_str_hex_no_prefix_happy_path() {
|
||||
let lock_time = LockTime::from_hex_str_no_prefix("BA70D").unwrap();
|
||||
assert_eq!(lock_time, LockTime::from_consensus(0xBA70D));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packed_lock_time_from_str_hex_invalid_hex_should_ergr() {
|
||||
let hex = "0xzb93";
|
||||
let result = LockTime::from_hex_str(hex);
|
||||
let result = Time::from_hex(hex);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn height_from_str_hex_happy_path() {
|
||||
let actual = Height::from_hex_str("0xBA70D").unwrap();
|
||||
let actual = Height::from_hex("0xBA70D").unwrap();
|
||||
let expected = Height(0xBA70D);
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn height_from_str_hex_no_prefix_happy_path() {
|
||||
let height = Height::from_hex_str_no_prefix("BA70D").unwrap();
|
||||
let height = Height::from_hex("BA70D").unwrap();
|
||||
assert_eq!(height, Height(0xBA70D));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn height_from_str_hex_invalid_hex_should_err() {
|
||||
let hex = "0xzb93";
|
||||
let result = Height::from_hex_str(hex);
|
||||
let result = Height::from_hex(hex);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,11 +24,11 @@ use crate::blockdata::script::{Script, ScriptBuf};
|
|||
use crate::blockdata::witness::Witness;
|
||||
use crate::blockdata::FeeRate;
|
||||
use crate::consensus::{encode, Decodable, Encodable};
|
||||
use crate::error::{PrefixedHexError, UnprefixedHexError, ContainsPrefixError, MissingPrefixError};
|
||||
use crate::internal_macros::{impl_consensus_encoding, impl_hashencode};
|
||||
use crate::parse::impl_parse_str_from_int_infallible;
|
||||
use crate::parse::{self, impl_parse_str_from_int_infallible};
|
||||
#[cfg(doc)]
|
||||
use crate::sighash::{EcdsaSighashType, TapSighashType};
|
||||
use crate::string::FromHexStr;
|
||||
use crate::prelude::*;
|
||||
use crate::{Amount, SignedAmount, VarInt};
|
||||
|
||||
|
|
@ -402,6 +402,29 @@ impl Sequence {
|
|||
self.is_relative_lock_time() & (self.0 & Sequence::LOCK_TYPE_MASK > 0)
|
||||
}
|
||||
|
||||
/// Creates a `Sequence` from an prefixed hex string.
|
||||
pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
|
||||
let stripped = if let Some(stripped) = s.strip_prefix("0x") {
|
||||
stripped
|
||||
} 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.
|
||||
pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
|
||||
if s.starts_with("0x") || s.starts_with("0X") {
|
||||
return Err(ContainsPrefixError::new(s).into());
|
||||
}
|
||||
let lock_time = parse::hex_u32(s)?;
|
||||
Ok(Self::from_consensus(lock_time))
|
||||
}
|
||||
|
||||
/// Creates a relative lock-time using block height.
|
||||
#[inline]
|
||||
pub fn from_height(height: u16) -> Self { Sequence(u32::from(height)) }
|
||||
|
|
@ -473,15 +496,6 @@ impl Sequence {
|
|||
fn low_u16(&self) -> u16 { self.0 as u16 }
|
||||
}
|
||||
|
||||
impl FromHexStr for Sequence {
|
||||
type Error = crate::parse::ParseIntError;
|
||||
|
||||
fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Self::Error> {
|
||||
let sequence = crate::parse::hex_u32(s)?;
|
||||
Ok(Self::from_consensus(sequence))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Sequence {
|
||||
/// The default value of sequence is 0xffffffff.
|
||||
fn default() -> Self { Sequence::MAX }
|
||||
|
|
@ -2119,21 +2133,33 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn sequence_from_str_hex_happy_path() {
|
||||
let sequence = Sequence::from_hex_str("0xFFFFFFFF").unwrap();
|
||||
fn sequence_from_hex_lower() {
|
||||
let sequence = Sequence::from_hex("0xffffffff").unwrap();
|
||||
assert_eq!(sequence, Sequence::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sequence_from_str_hex_no_prefix_happy_path() {
|
||||
let sequence = Sequence::from_hex_str_no_prefix("FFFFFFFF").unwrap();
|
||||
fn sequence_from_hex_upper() {
|
||||
let sequence = Sequence::from_hex("0XFFFFFFFF").unwrap();
|
||||
assert_eq!(sequence, Sequence::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sequence_from_unprefixed_hex_lower() {
|
||||
let sequence = Sequence::from_unprefixed_hex("ffffffff").unwrap();
|
||||
assert_eq!(sequence, Sequence::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sequence_from_unprefixed_hex_upper() {
|
||||
let sequence = Sequence::from_unprefixed_hex("FFFFFFFF").unwrap();
|
||||
assert_eq!(sequence, Sequence::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sequence_from_str_hex_invalid_hex_should_err() {
|
||||
let hex = "0xzb93";
|
||||
let result = Sequence::from_hex_str(hex);
|
||||
let result = Sequence::from_hex(hex);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,131 @@
|
|||
|
||||
//! 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.
|
||||
#[doc(inline)]
|
||||
pub use crate::parse::ParseIntError;
|
||||
|
||||
/// 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 {}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,6 @@ pub mod policy;
|
|||
pub mod pow;
|
||||
pub mod psbt;
|
||||
pub mod sign_message;
|
||||
pub mod string;
|
||||
pub mod taproot;
|
||||
|
||||
#[rustfmt::skip] // Keep public re-exports separate.
|
||||
|
|
|
|||
|
|
@ -90,8 +90,19 @@ pub(crate) fn int<T: Integer, S: AsRef<str> + Into<String>>(s: S) -> Result<T, P
|
|||
})
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn hex_u32<S: AsRef<str> + Into<String>>(s: S) -> Result<u32, ParseIntError> {
|
||||
u32::from_str_radix(s.as_ref(), 16).map_err(|error| ParseIntError {
|
||||
u32::from_str_radix(strip_hex_prefix(s.as_ref()), 16).map_err(|error| ParseIntError {
|
||||
input: s.into(),
|
||||
bits: u8::try_from(core::mem::size_of::<u32>() * 8).expect("max is 32 bits for u32"),
|
||||
is_signed: u32::try_from(-1i8).is_ok(),
|
||||
|
|
|
|||
|
|
@ -17,9 +17,8 @@ use crate::blockdata::block::BlockHash;
|
|||
use crate::consensus::encode::{self, Decodable, Encodable};
|
||||
#[cfg(doc)]
|
||||
use crate::consensus::Params;
|
||||
use crate::string::FromHexStr;
|
||||
use crate::prelude::*;
|
||||
use crate::Network;
|
||||
use crate::error::{PrefixedHexError, UnprefixedHexError, ContainsPrefixError, MissingPrefixError};
|
||||
use crate::{parse, Network};
|
||||
|
||||
/// Implement traits and methods shared by `Target` and `Work`.
|
||||
macro_rules! do_impl {
|
||||
|
|
@ -272,6 +271,29 @@ do_impl!(Target);
|
|||
pub struct CompactTarget(u32);
|
||||
|
||||
impl CompactTarget {
|
||||
/// Creates a `CompactTarget` from an prefixed hex string.
|
||||
pub fn from_hex(s: &str) -> Result<Self, PrefixedHexError> {
|
||||
let stripped = if let Some(stripped) = s.strip_prefix("0x") {
|
||||
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))
|
||||
}
|
||||
|
||||
/// Creates a `CompactTarget` from an unprefixed hex string.
|
||||
pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
|
||||
if s.starts_with("0x") || s.starts_with("0X") {
|
||||
return Err(ContainsPrefixError::new(s).into());
|
||||
}
|
||||
let lock_time = parse::hex_u32(s)?;
|
||||
Ok(Self::from_consensus(lock_time))
|
||||
}
|
||||
|
||||
/// Creates a [`CompactTarget`] from a consensus encoded `u32`.
|
||||
pub fn from_consensus(bits: u32) -> Self { Self(bits) }
|
||||
|
||||
|
|
@ -283,15 +305,6 @@ impl From<CompactTarget> for Target {
|
|||
fn from(c: CompactTarget) -> Self { Target::from_compact(c) }
|
||||
}
|
||||
|
||||
impl FromHexStr for CompactTarget {
|
||||
type Error = crate::parse::ParseIntError;
|
||||
|
||||
fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Self::Error> {
|
||||
let compact_target = crate::parse::hex_u32(s)?;
|
||||
Ok(Self::from_consensus(compact_target))
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for CompactTarget {
|
||||
#[inline]
|
||||
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
||||
|
|
@ -1524,23 +1537,33 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn compact_target_from_hex_str_happy_path() {
|
||||
let actual = CompactTarget::from_hex_str("0x01003456").unwrap();
|
||||
let expected = CompactTarget(0x01003456);
|
||||
assert_eq!(actual, expected);
|
||||
fn compact_target_from_hex_lower() {
|
||||
let target = CompactTarget::from_hex("0x010034ab").unwrap();
|
||||
assert_eq!(target, CompactTarget(0x010034ab));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_target_from_hex_str_no_prefix_happy_path() {
|
||||
let actual = CompactTarget::from_hex_str_no_prefix("01003456").unwrap();
|
||||
let expected = CompactTarget(0x01003456);
|
||||
assert_eq!(actual, expected);
|
||||
fn compact_target_from_hex_upper() {
|
||||
let target = CompactTarget::from_hex("0X010034AB").unwrap();
|
||||
assert_eq!(target, CompactTarget(0x010034ab));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_target_from_unprefixed_hex_lower() {
|
||||
let target = CompactTarget::from_unprefixed_hex("010034ab").unwrap();
|
||||
assert_eq!(target, CompactTarget(0x010034ab));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_target_from_unprefixed_hex_upper() {
|
||||
let target = CompactTarget::from_unprefixed_hex("010034AB").unwrap();
|
||||
assert_eq!(target, CompactTarget(0x010034ab));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_target_from_hex_invalid_hex_should_err() {
|
||||
let hex = "0xzbf9";
|
||||
let result = CompactTarget::from_hex_str(hex);
|
||||
let result = CompactTarget::from_hex(hex);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
//! Bitcoin string parsing utilities.
|
||||
//!
|
||||
//! This module provides utility types and traits
|
||||
//! to support handling and parsing strings within `rust-bitcoin`.
|
||||
|
||||
use core::fmt;
|
||||
|
||||
use internals::write_err;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Trait that allows types to be initialized from hex strings
|
||||
pub trait FromHexStr: Sized {
|
||||
/// An error occurred while parsing the hex string.
|
||||
type Error;
|
||||
|
||||
/// Parses provided string as hex requiring 0x prefix.
|
||||
///
|
||||
/// This is intended for user-supplied inputs or already-existing protocols in which 0x prefix is used.
|
||||
fn from_hex_str<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, FromHexError<Self::Error>> {
|
||||
if !s.as_ref().starts_with("0x") {
|
||||
Err(FromHexError::MissingPrefix(s.into()))
|
||||
} else {
|
||||
Ok(Self::from_hex_str_no_prefix(s.as_ref().trim_start_matches("0x"))?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses provided string as hex without requiring 0x prefix.
|
||||
///
|
||||
/// This is **not** recommended for user-supplied inputs because of possible confusion with decimals.
|
||||
/// It should be only used for existing protocols which always encode values as hex without 0x prefix.
|
||||
fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Self::Error>;
|
||||
}
|
||||
|
||||
/// Hex parsing error
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub enum FromHexError<E> {
|
||||
/// The input was not a valid hex string, contains the error that occurred while parsing.
|
||||
ParseHex(E),
|
||||
/// The input is missing `0x` prefix, contains the invalid input.
|
||||
MissingPrefix(String),
|
||||
}
|
||||
|
||||
impl<E> From<E> for FromHexError<E> {
|
||||
fn from(e: E) -> Self { FromHexError::ParseHex(e) }
|
||||
}
|
||||
|
||||
impl<E: fmt::Display> fmt::Display for FromHexError<E> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use FromHexError::*;
|
||||
|
||||
match *self {
|
||||
ParseHex(ref e) => write_err!(f, "failed to parse hex string"; e),
|
||||
MissingPrefix(ref value) =>
|
||||
write_err!(f, "the input value `{}` is missing the `0x` prefix", value; self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<E> std::error::Error for FromHexError<E>
|
||||
where
|
||||
E: std::error::Error + 'static,
|
||||
{
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
use FromHexError::*;
|
||||
|
||||
match *self {
|
||||
ParseHex(ref e) => Some(e),
|
||||
MissingPrefix(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue