tendermint/block/
height.rs

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