Improve string parsing

Currently we implement string parsing for height/time from the
`absolute` module but not the `relative` module.

Improve the macros used to implement string parsing and use the new
versions to implement string parsing for the height and time types in
`relative`.
This commit is contained in:
Tobin C. Harding 2023-02-06 17:15:47 +11:00
parent b6387db47f
commit 090dad770f
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
6 changed files with 73 additions and 80 deletions

View File

@ -2,6 +2,8 @@
use core::fmt; use core::fmt;
use core::ops::{Mul, Div}; use core::ops::{Mul, Div};
use crate::prelude::*;
use crate::Amount; use crate::Amount;
use super::Weight; use super::Weight;
@ -133,4 +135,4 @@ impl Div<Weight> for Amount {
} }
} }
crate::parse::impl_parse_str_through_int!(FeeRate); crate::parse::impl_parse_str_from_int_infallible!(FeeRate, u64, from_sat_per_kwu);

View File

@ -9,8 +9,6 @@
use core::{mem, fmt}; use core::{mem, fmt};
use core::cmp::{PartialOrd, Ordering}; use core::cmp::{PartialOrd, Ordering};
use core::convert::TryFrom;
use core::str::FromStr;
use bitcoin_internals::write_err; use bitcoin_internals::write_err;
@ -20,8 +18,8 @@ use mutagen::mutate;
use crate::consensus::encode::{self, Decodable, Encodable}; use crate::consensus::encode::{self, Decodable, Encodable};
use crate::error::ParseIntError; use crate::error::ParseIntError;
use crate::io::{self, Read, Write}; use crate::io::{self, Read, Write};
use crate::parse::{impl_parse_str_from_int_infallible, impl_parse_str_from_int_fallible};
use crate::prelude::*; use crate::prelude::*;
use crate::parse::{self, impl_parse_str_through_int};
use crate::string::FromHexStr; use crate::string::FromHexStr;
#[cfg(doc)] #[cfg(doc)]
@ -276,7 +274,7 @@ impl LockTime {
} }
} }
impl_parse_str_through_int!(LockTime, from_consensus); impl_parse_str_from_int_infallible!(LockTime, u32, from_consensus);
impl From<Height> for LockTime { impl From<Height> for LockTime {
#[inline] #[inline]
@ -451,6 +449,8 @@ impl Height {
} }
} }
impl_parse_str_from_int_fallible!(Height, u32, from_consensus, Error);
impl fmt::Display for Height { impl fmt::Display for Height {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f) fmt::Display::fmt(&self.0, f)
@ -467,37 +467,6 @@ impl FromHexStr for Height {
} }
} }
impl FromStr for Height {
type Err = Error;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
let n = parse::int(s)?;
Height::from_consensus(n)
}
}
impl TryFrom<&str> for Height {
type Error = Error;
#[inline]
fn try_from(s: &str) -> Result<Self, Self::Error> {
let n = parse::int(s)?;
Height::from_consensus(n)
}
}
impl TryFrom<String> for Height {
type Error = Error;
#[inline]
fn try_from(s: String) -> Result<Self, Self::Error> {
let n = parse::int(s)?;
Height::from_consensus(n)
}
}
/// A UNIX timestamp, seconds since epoch, guaranteed to always contain a valid time value. /// A UNIX timestamp, seconds since epoch, guaranteed to always contain a valid time value.
/// ///
/// Note that there is no manipulation of the inner value during construction or when using /// Note that there is no manipulation of the inner value during construction or when using
@ -564,6 +533,8 @@ impl Time {
} }
} }
impl_parse_str_from_int_fallible!(Time, u32, from_consensus, Error);
impl fmt::Display for Time { impl fmt::Display for Time {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f) fmt::Display::fmt(&self.0, f)
@ -580,36 +551,6 @@ impl FromHexStr for Time {
} }
} }
impl FromStr for Time {
type Err = Error;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
let n = parse::int(s)?;
Time::from_consensus(n)
}
}
impl TryFrom<&str> for Time {
type Error = Error;
#[inline]
fn try_from(s: &str) -> Result<Self, Self::Error> {
let n = parse::int(s)?;
Time::from_consensus(n)
}
}
impl TryFrom<String> for Time {
type Error = Error;
#[inline]
fn try_from(s: String) -> Result<Self, Self::Error> {
let n = parse::int(s)?;
Time::from_consensus(n)
}
}
/// Returns true if `n` is a block height i.e., less than 500,000,000. /// Returns true if `n` is a block height i.e., less than 500,000,000.
fn is_block_height(n: u32) -> bool { fn is_block_height(n: u32) -> bool {
n < LOCK_TIME_THRESHOLD n < LOCK_TIME_THRESHOLD

View File

@ -13,6 +13,9 @@ use core::convert::TryFrom;
#[cfg(all(test, mutate))] #[cfg(all(test, mutate))]
use mutagen::mutate; use mutagen::mutate;
use crate::parse::impl_parse_str_from_int_infallible;
use crate::prelude::*;
#[cfg(doc)] #[cfg(doc)]
use crate::relative; use crate::relative;
@ -236,6 +239,8 @@ impl From<u16> for Height {
} }
} }
impl_parse_str_from_int_infallible!(Height, u16, from);
impl fmt::Display for Height { impl fmt::Display for Height {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f) fmt::Display::fmt(&self.0, f)
@ -300,6 +305,8 @@ impl Time {
} }
} }
impl_parse_str_from_int_infallible!(Time, u16, from_512_second_intervals);
impl fmt::Display for Time { impl fmt::Display for Time {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f) fmt::Display::fmt(&self.0, f)

View File

@ -33,7 +33,7 @@ use crate::consensus::{encode, Decodable, Encodable};
use crate::hash_types::{Sighash, Txid, Wtxid}; use crate::hash_types::{Sighash, Txid, Wtxid};
use crate::VarInt; use crate::VarInt;
use crate::internal_macros::impl_consensus_encoding; use crate::internal_macros::impl_consensus_encoding;
use crate::parse::impl_parse_str_through_int; use crate::parse::impl_parse_str_from_int_infallible;
use super::Weight; use super::Weight;
#[cfg(doc)] #[cfg(doc)]
@ -499,7 +499,7 @@ impl fmt::UpperHex for Sequence {
} }
} }
impl_parse_str_through_int!(Sequence); impl_parse_str_from_int_infallible!(Sequence, u32, from_consensus);
/// Bitcoin transaction output. /// Bitcoin transaction output.
/// ///

View File

@ -3,6 +3,8 @@
use core::fmt; use core::fmt;
use core::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign}; use core::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign};
use crate::prelude::*;
/// Represents block weight - the weight of a transaction or block. /// Represents block weight - the weight of a transaction or block.
/// ///
/// This is an integer newtype representing weigth in `wu`. It provides protection against mixing /// This is an integer newtype representing weigth in `wu`. It provides protection against mixing
@ -195,4 +197,4 @@ impl<'a> core::iter::Sum<&'a Weight> for Weight {
} }
} }
crate::parse::impl_parse_str_through_int!(Weight); crate::parse::impl_parse_str_from_int_infallible!(Weight, u64, from_wu);

View File

@ -95,37 +95,78 @@ pub(crate) fn hex_u32<S: AsRef<str> + Into<String>>(s: S) -> Result<u32, ParseIn
impl_std_error!(ParseIntError, source); impl_std_error!(ParseIntError, source);
/// Implements `TryFrom<$from> for $to` using `parse::int`, mapping the output using `fn` /// Implements `TryFrom<$from> for $to` using `parse::int`, mapping the output using infallible
macro_rules! impl_tryfrom_str_through_int_single { /// conversion function `fn`.
($($from:ty, $to:ident $(, $fn:ident)?);*) => { macro_rules! impl_tryfrom_str_from_int_infallible {
($($from:ty, $to:ident, $inner:ident, $fn:ident);*) => {
$( $(
impl core::convert::TryFrom<$from> for $to { impl core::convert::TryFrom<$from> for $to {
type Error = $crate::error::ParseIntError; type Error = $crate::error::ParseIntError;
fn try_from(s: $from) -> Result<Self, Self::Error> { fn try_from(s: $from) -> Result<Self, Self::Error> {
$crate::parse::int(s).map($to $(:: $fn)?) $crate::parse::int::<$inner, $from>(s).map($to::$fn)
} }
} }
)* )*
} }
} }
pub(crate) use impl_tryfrom_str_through_int_single; pub(crate) use impl_tryfrom_str_from_int_infallible;
/// Implements `FromStr` and `TryFrom<{&str, String, Box<str>}> for $to` using `parse::int`, mapping the output using `fn` /// Implements `FromStr` and `TryFrom<{&str, String, Box<str>}> for $to` using `parse::int`, mapping
/// the output using infallible conversion function `fn`.
/// ///
/// The `Error` type is `ParseIntError` /// The `Error` type is `ParseIntError`
macro_rules! impl_parse_str_through_int { macro_rules! impl_parse_str_from_int_infallible {
($to:ident $(, $fn:ident)?) => { ($to:ident, $inner:ident, $fn:ident) => {
$crate::parse::impl_tryfrom_str_through_int_single!(&str, $to $(, $fn)?; alloc::string::String, $to $(, $fn)?; alloc::boxed::Box<str>, $to $(, $fn)?); $crate::parse::impl_tryfrom_str_from_int_infallible!(&str, $to, $inner, $fn; String, $to, $inner, $fn; Box<str>, $to, $inner, $fn);
impl core::str::FromStr for $to { impl core::str::FromStr for $to {
type Err = $crate::error::ParseIntError; type Err = $crate::error::ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
$crate::parse::int(s).map($to $(:: $fn)?) $crate::parse::int::<$inner, &str>(s).map($to::$fn)
} }
} }
} }
} }
pub(crate) use impl_parse_str_through_int; pub(crate) use impl_parse_str_from_int_infallible;
/// Implements `TryFrom<$from> for $to` using `parse::int`, mapping the output using fallible
/// conversion function `fn`.
macro_rules! impl_tryfrom_str_from_int_fallible {
($($from:ty, $to:ident, $inner:ident, $fn:ident, $err:ident);*) => {
$(
impl core::convert::TryFrom<$from> for $to {
type Error = $err;
fn try_from(s: $from) -> Result<Self, Self::Error> {
let u = $crate::parse::int::<$inner, $from>(s)?;
$to::$fn(u)
}
}
)*
}
}
pub(crate) use impl_tryfrom_str_from_int_fallible;
/// Implements `FromStr` and `TryFrom<{&str, String, Box<str>}> for $to` using `parse::int`, mapping
/// the output using fallible conversion function `fn`.
///
/// The `Error` type is `ParseIntError`
macro_rules! impl_parse_str_from_int_fallible {
($to:ident, $inner:ident, $fn:ident, $err:ident) => {
$crate::parse::impl_tryfrom_str_from_int_fallible!(&str, $to, $inner, $fn, $err; String, $to, $inner, $fn, $err; Box<str>, $to, $inner, $fn, $err);
impl core::str::FromStr for $to {
type Err = $err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let u = $crate::parse::int::<$inner, &str>(s)?;
$to::$fn(u)
}
}
}
}
pub(crate) use impl_parse_str_from_int_fallible;