use core::{
fmt::{self, Display},
str::{self, FromStr},
};
use serde::{Deserialize, Serialize};
use tendermint_proto::v0_37::types::BlockId as RawBlockId;
use crate::{
block::parts::Header as PartSetHeader,
error::Error,
hash::{Algorithm, Hash},
prelude::*,
};
pub const PREFIX_LENGTH: usize = 10;
#[derive(
Serialize, Deserialize, Copy, Clone, Debug, Default, Hash, Eq, PartialEq, PartialOrd, Ord,
)]
#[serde(try_from = "RawBlockId", into = "RawBlockId")]
pub struct Id {
pub hash: Hash,
pub part_set_header: PartSetHeader,
}
tendermint_pb_modules! {
use pb::{
types::{
BlockId as RawBlockId, CanonicalBlockId as RawCanonicalBlockId,
PartSetHeader as RawPartSetHeader,
}
};
use super::Id;
use crate::{prelude::*, Error};
impl Protobuf<RawBlockId> for Id {}
impl TryFrom<RawBlockId> for Id {
type Error = Error;
fn try_from(value: RawBlockId) -> Result<Self, Self::Error> {
if value.part_set_header.is_none() {
return Err(Error::invalid_part_set_header(
"part_set_header is None".to_string(),
));
}
Ok(Self {
hash: value.hash.try_into()?,
part_set_header: value.part_set_header.unwrap().try_into()?,
})
}
}
impl From<Id> for RawBlockId {
fn from(value: Id) -> Self {
if value == Id::default() {
RawBlockId {
hash: vec![],
part_set_header: Some(RawPartSetHeader {
total: 0,
hash: vec![],
}),
}
} else {
RawBlockId {
hash: value.hash.into(),
part_set_header: Some(value.part_set_header.into()),
}
}
}
}
impl TryFrom<RawCanonicalBlockId> for Id {
type Error = Error;
fn try_from(value: RawCanonicalBlockId) -> Result<Self, Self::Error> {
if value.part_set_header.is_none() {
return Err(Error::invalid_part_set_header(
"part_set_header is None".to_string(),
));
}
Ok(Self {
hash: value.hash.try_into()?,
part_set_header: value.part_set_header.unwrap().try_into()?,
})
}
}
impl From<Id> for RawCanonicalBlockId {
fn from(value: Id) -> Self {
RawCanonicalBlockId {
hash: value.hash.as_bytes().to_vec(),
part_set_header: Some(value.part_set_header.into()),
}
}
}
}
impl Id {
pub fn prefix(&self) -> String {
let mut result = self.to_string();
result.truncate(PREFIX_LENGTH);
result
}
}
impl Display for Id {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", &self.hash)
}
}
impl FromStr for Id {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Error> {
Ok(Self {
hash: Hash::from_hex_upper(Algorithm::Sha256, s)?,
part_set_header: PartSetHeader::default(),
})
}
}
pub trait ParseId {
fn parse_block_id(&self) -> Result<Id, Error>;
}
#[cfg(test)]
mod tests {
use super::*;
const EXAMPLE_SHA256_ID: &str =
"26C0A41F3243C6BCD7AD2DFF8A8D83A71D29D307B5326C227F734A1A512FE47D";
#[test]
fn parses_hex_strings() {
let id = Id::from_str(EXAMPLE_SHA256_ID).unwrap();
assert_eq!(
id.hash.as_bytes(),
b"\x26\xC0\xA4\x1F\x32\x43\xC6\xBC\xD7\xAD\x2D\xFF\x8A\x8D\x83\xA7\
\x1D\x29\xD3\x07\xB5\x32\x6C\x22\x7F\x73\x4A\x1A\x51\x2F\xE4\x7D"
.as_ref()
);
}
#[test]
fn serializes_hex_strings() {
let id = Id::from_str(EXAMPLE_SHA256_ID).unwrap();
assert_eq!(&id.to_string(), EXAMPLE_SHA256_ID)
}
}