Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions crates/chain/src/rusqlite_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,20 @@ impl ToSql for Impl<bitcoin::BlockHash> {
}
}

impl FromSql for Impl<BlockId> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
BlockId::from_str(value.as_str()?)
.map(Self)
.map_err(from_sql_error)
}
}

impl ToSql for Impl<BlockId> {
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
Ok(self.to_string().into())
}
}

#[cfg(feature = "miniscript")]
impl FromSql for Impl<DescriptorId> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
Expand Down
54 changes: 54 additions & 0 deletions crates/core/src/block_id.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use core::fmt::{self, Display};
use core::str::FromStr;

use bitcoin::{hashes::Hash, BlockHash};

/// A reference to a block in the canonical chain.
Expand Down Expand Up @@ -40,6 +43,57 @@ impl From<(&u32, &BlockHash)> for BlockId {
}
}

impl Display for BlockId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}:{}", self.height, self.hash)
}
}

impl FromStr for BlockId {
type Err = ParseBlockIdError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let (height_str, hash_str) = s.split_once(':').ok_or(ParseBlockIdError::InvalidFormat)?;

let height = height_str
.parse::<u32>()
.map_err(|_| ParseBlockIdError::InvalidHeight)?;

let hash = hash_str
.parse::<BlockHash>()
.map_err(|_| ParseBlockIdError::InvalidBlockhash)?;

Ok(BlockId { height, hash })
}
}

/// [`BlockId`] parsing errors.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseBlockIdError {
/// Invalid [`BlockId`] representation (missing `:` separator).
InvalidFormat,
/// Invalid block height (failed to parse into a `u32`).
InvalidHeight,
/// Invalid block hash (failed to parse into a [`Blockhash`]).
InvalidBlockhash,
}

impl Display for ParseBlockIdError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidFormat => write!(
f,
"Failed to parse string into `BlockId`, expected `<height>:<hash>`"
),
Self::InvalidHeight => write!(f, "Failed to parse height into a u32."),
Self::InvalidBlockhash => write!(f, "Failed to parse hash into a `Blockhash`."),
}
}
}

#[cfg(feature = "std")]
impl std::error::Error for ParseBlockIdError {}

/// Represents the confirmation block and time of a transaction.
#[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
Expand Down