Merge rust-bitcoin/rust-bitcoin#1623: Improve string parsing

090dad770f Improve string parsing (Tobin C. Harding)

Pull request description:

  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`.

  Done while reviewing data structures in relation to `serde`.

ACKs for top commit:
  apoelstra:
    ACK 090dad770f
  Kixunil:
    ACK 090dad770f

Tree-SHA512: bfa88efbaf5dc35755eb46df373a08e223f112860e8a65f58db9fdd77e2c01dc9377da735b33ef58940004fe5fe11690ac09be19591fded2c9fd04cd7d2bdf73
This commit is contained in:
Andrew Poelstra 2023-02-25 20:23:37 +00:00
commit 5a867821aa
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
6 changed files with 73 additions and 80 deletions

View File

@ -2,6 +2,8 @@
use core::fmt;
use core::ops::{Mul, Div};
use crate::prelude::*;
use crate::Amount;
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::cmp::{PartialOrd, Ordering};
use core::convert::TryFrom;
use core::str::FromStr;
use bitcoin_internals::write_err;
@ -20,8 +18,8 @@ use mutagen::mutate;
use crate::consensus::encode::{self, Decodable, Encodable};
use crate::error::ParseIntError;
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::parse::{self, impl_parse_str_through_int};
use crate::string::FromHexStr;
#[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 {
#[inline]
@ -451,6 +449,8 @@ impl Height {
}
}
impl_parse_str_from_int_fallible!(Height, u32, from_consensus, Error);
impl fmt::Display for Height {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
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.
///
/// 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 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
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.
fn is_block_height(n: u32) -> bool {
n < LOCK_TIME_THRESHOLD

View File

@ -13,6 +13,9 @@ use core::convert::TryFrom;
#[cfg(all(test, mutate))]
use mutagen::mutate;
use crate::parse::impl_parse_str_from_int_infallible;
use crate::prelude::*;
#[cfg(doc)]
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 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
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 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)

View File

@ -34,7 +34,7 @@ use crate::crypto::sighash::LegacySighash;
use crate::hash_types::{Txid, Wtxid};
use crate::VarInt;
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;
#[cfg(doc)]
@ -500,7 +500,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.
///

View File

@ -3,6 +3,8 @@
use core::fmt;
use core::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign};
use crate::prelude::*;
/// Represents block weight - the weight of a transaction or block.
///
/// 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);
/// Implements `TryFrom<$from> for $to` using `parse::int`, mapping the output using `fn`
macro_rules! impl_tryfrom_str_through_int_single {
($($from:ty, $to:ident $(, $fn:ident)?);*) => {
/// Implements `TryFrom<$from> for $to` using `parse::int`, mapping the output using infallible
/// conversion function `fn`.
macro_rules! impl_tryfrom_str_from_int_infallible {
($($from:ty, $to:ident, $inner:ident, $fn:ident);*) => {
$(
impl core::convert::TryFrom<$from> for $to {
type Error = $crate::error::ParseIntError;
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`
macro_rules! impl_parse_str_through_int {
($to: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)?);
macro_rules! impl_parse_str_from_int_infallible {
($to:ident, $inner:ident, $fn:ident) => {
$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 {
type Err = $crate::error::ParseIntError;
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;