iq_cometbft/block/
height.rs

1use core::{
2    convert::{TryFrom, TryInto},
3    fmt::{self, Debug, Display},
4    str::FromStr,
5};
6
7use cometbft_proto::Protobuf;
8use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};
9
10use crate::{error::Error, prelude::*};
11
12/// Block height for a particular chain (i.e. number of blocks created since
13/// the chain began)
14///
15/// A height of 0 represents a chain which has not yet produced a block.
16#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
17pub struct Height(u64);
18
19impl Protobuf<i64> for Height {}
20
21impl TryFrom<i64> for Height {
22    type Error = Error;
23
24    fn try_from(value: i64) -> Result<Self, Self::Error> {
25        Ok(Height(value.try_into().map_err(Error::negative_height)?))
26    }
27}
28
29impl From<Height> for i64 {
30    fn from(value: Height) -> Self {
31        value.value() as i64 // does not overflow. The value is <= i64::MAX
32    }
33}
34
35impl TryFrom<u64> for Height {
36    type Error = Error;
37
38    fn try_from(value: u64) -> Result<Self, Self::Error> {
39        // Make sure the u64 value can be converted safely to i64
40        let _ival: i64 = value.try_into().map_err(Error::integer_overflow)?;
41
42        Ok(Height(value))
43    }
44}
45
46impl From<Height> for u64 {
47    fn from(value: Height) -> Self {
48        value.value()
49    }
50}
51
52impl From<u32> for Height {
53    fn from(value: u32) -> Self {
54        Height(value as u64)
55    }
56}
57
58impl From<u16> for Height {
59    fn from(value: u16) -> Self {
60        Height(value as u64)
61    }
62}
63
64impl From<u8> for Height {
65    fn from(value: u8) -> Self {
66        Height(value as u64)
67    }
68}
69
70impl Height {
71    /// Get inner integer value. Alternative to `.0` or `.into()`
72    pub fn value(&self) -> u64 {
73        self.0
74    }
75
76    /// Increment the block height by 1
77    pub fn increment(self) -> Self {
78        Height::try_from(self.0.checked_add(1).expect("height overflow")).unwrap()
79    }
80}
81
82impl Debug for Height {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        write!(f, "block::Height({})", self.0)
85    }
86}
87
88impl Default for Height {
89    fn default() -> Self {
90        Height(1)
91    }
92}
93
94impl Display for Height {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        write!(f, "{}", self.0)
97    }
98}
99
100impl FromStr for Height {
101    type Err = Error;
102
103    fn from_str(s: &str) -> Result<Self, Error> {
104        Height::try_from(
105            s.parse::<u64>()
106                .map_err(|e| Error::parse_int(s.to_string(), e))?,
107        )
108    }
109}
110
111impl<'de> Deserialize<'de> for Height {
112    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
113        Self::from_str(&String::deserialize(deserializer)?)
114            .map_err(|e| D::Error::custom(format!("{e}")))
115    }
116}
117
118impl Serialize for Height {
119    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
120        i64::from(*self).to_string().serialize(serializer)
121    }
122}
123
124/// Parse `block::Height` from a type
125pub trait ParseHeight {
126    /// Parse `block::Height`, or return an `Error` if parsing failed
127    fn parse_block_height(&self) -> Result<Height, Error>;
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn increment_by_one() {
136        assert_eq!(Height::default().increment().value(), 2);
137    }
138
139    #[test]
140    fn avoid_try_unwrap_dance() {
141        assert_eq!(
142            Height::try_from(2_u64).unwrap().value(),
143            Height::from(2_u32).value()
144        );
145    }
146}