Skip to main content

amaru_kernel/cardano/
epoch.rs

1// Copyright 2026 PRAGMA
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{
16    fmt,
17    ops::{Add, Sub},
18    str::FromStr,
19};
20
21use minicbor::{Decode, Decoder, Encode};
22#[cfg(any(test, feature = "test-utils"))]
23use proptest::prelude::{Arbitrary, BoxedStrategy, Strategy};
24
25#[derive(Clone, Debug, Copy, PartialEq, PartialOrd, Ord, Eq, serde::Serialize, serde::Deserialize, Default)]
26#[repr(transparent)]
27pub struct Epoch(u64);
28
29impl Epoch {
30    pub fn new(epoch: u64) -> Self {
31        Self(epoch)
32    }
33
34    pub fn as_u64(&self) -> u64 {
35        self.0
36    }
37}
38
39#[cfg(any(test, feature = "test-utils"))]
40impl Arbitrary for Epoch {
41    type Parameters = ();
42    type Strategy = BoxedStrategy<Self>;
43
44    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
45        (0..u64::MAX).prop_map(Epoch::from).boxed()
46    }
47}
48
49impl fmt::Display for Epoch {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        write!(f, "{}", self.0)
52    }
53}
54
55impl FromStr for Epoch {
56    type Err = std::num::ParseIntError;
57
58    fn from_str(s: &str) -> Result<Self, Self::Err> {
59        s.parse::<u64>().map(Epoch)
60    }
61}
62
63impl From<u64> for Epoch {
64    fn from(epoch: u64) -> Epoch {
65        Epoch(epoch)
66    }
67}
68
69impl From<Epoch> for u64 {
70    fn from(epoch: Epoch) -> u64 {
71        epoch.0
72    }
73}
74
75impl<C> Encode<C> for Epoch {
76    fn encode<W: minicbor::encode::Write>(
77        &self,
78        e: &mut minicbor::Encoder<W>,
79        ctx: &mut C,
80    ) -> Result<(), minicbor::encode::Error<W::Error>> {
81        self.0.encode(e, ctx)
82    }
83}
84
85impl<'b, C> Decode<'b, C> for Epoch {
86    fn decode(d: &mut Decoder<'b>, _ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
87        d.u64().map(Epoch)
88    }
89}
90
91impl Add<u64> for Epoch {
92    type Output = Self;
93
94    fn add(self, rhs: u64) -> Self::Output {
95        Epoch(self.0 + rhs)
96    }
97}
98
99impl Sub<u64> for Epoch {
100    type Output = Self;
101
102    fn sub(self, rhs: u64) -> Self::Output {
103        Epoch(self.0 - rhs)
104    }
105}
106
107impl Epoch {
108    pub fn saturating_sub(self, rhs: u64) -> Self {
109        Self(self.0.saturating_sub(rhs))
110    }
111}
112
113impl Sub<Epoch> for Epoch {
114    type Output = u64;
115
116    fn sub(self, rhs: Epoch) -> Self::Output {
117        self.0 - rhs.0
118    }
119}
120
121#[cfg(any(test, feature = "test-utils"))]
122pub use tests::*;
123
124#[cfg(any(test, feature = "test-utils"))]
125mod tests {
126    use proptest::prelude::*;
127
128    use super::*;
129
130    prop_compose! {
131        pub fn any_epoch()(epoch in any::<u64>()) -> Epoch {
132            Epoch::from(epoch)
133        }
134    }
135}