celestia_core/block/
height.rs

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