pod_types/
time.rs

1use std::{
2    fmt::Display,
3    ops::{Add, Sub},
4    time::{Duration, SystemTime, UNIX_EPOCH},
5};
6
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
10pub enum TimestampError {
11    #[error(r#"invalid hex string "{0}": can't convert to Timestamp"#)]
12    InvalidHexString(String),
13}
14
15#[derive(
16    Clone,
17    Copy,
18    Debug,
19    PartialEq,
20    Eq,
21    PartialOrd,
22    Ord,
23    Serialize,
24    Deserialize,
25    std::hash::Hash,
26    Default,
27)]
28#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
29pub struct Timestamp(u128);
30
31impl Timestamp {
32    pub const MAX: Timestamp = Timestamp(u128::MAX);
33
34    pub fn zero() -> Self {
35        Timestamp(0)
36    }
37
38    pub fn as_micros(&self) -> u128 {
39        self.0
40    }
41
42    pub fn as_seconds(&self) -> u128 {
43        self.0 / 1_000_000
44    }
45
46    pub fn from_micros(micros: u128) -> Self {
47        Timestamp(micros)
48    }
49
50    pub fn from_seconds(seconds: u64) -> Self {
51        Timestamp(u128::from(seconds) * 1_000_000)
52    }
53
54    pub fn now() -> Self {
55        SystemClock.now()
56    }
57
58    pub fn from_hex_seconds_str(s: &str) -> Result<Self, TimestampError> {
59        match s {
60            "earliest" => Ok(Self::zero()),
61            "latest" | "finalized" => Ok(Self::now()),
62            // TODO: revisit the pending
63            "pending" => {
64                tracing::warn!(
65                    "Using 'pending' as a timestamp isn't properly implemented. Using current time."
66                );
67                Ok(Self::now())
68            }
69            s => Ok(Self::from_seconds(
70                u64::from_str_radix(s.strip_prefix("0x").unwrap_or(s), 16)
71                    .map_err(|_| TimestampError::InvalidHexString(s.to_string()))?,
72            )),
73        }
74    }
75}
76
77impl Display for Timestamp {
78    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79        write!(f, "{}", self.0)
80    }
81}
82
83impl From<SystemTime> for Timestamp {
84    fn from(value: SystemTime) -> Self {
85        Timestamp::from_micros(
86            value
87                .duration_since(UNIX_EPOCH)
88                .expect("Time went backwards")
89                .as_micros(),
90        )
91    }
92}
93
94impl From<Timestamp> for SystemTime {
95    fn from(value: Timestamp) -> Self {
96        UNIX_EPOCH + Duration::from_micros(value.as_micros() as u64)
97    }
98}
99
100impl Sub<Duration> for Timestamp {
101    type Output = Timestamp;
102
103    fn sub(self, rhs: Duration) -> Self::Output {
104        Timestamp(self.0 - rhs.as_micros())
105    }
106}
107
108impl Sub<Timestamp> for Timestamp {
109    type Output = Timestamp;
110
111    fn sub(self, rhs: Timestamp) -> Self::Output {
112        Timestamp::from_micros(self.0 - rhs.0)
113    }
114}
115
116impl Add<Duration> for Timestamp {
117    type Output = Timestamp;
118
119    fn add(self, rhs: Duration) -> Self::Output {
120        Timestamp(self.0 + rhs.as_micros())
121    }
122}
123pub trait Clock {
124    fn now(&self) -> Timestamp;
125}
126
127#[derive(Clone)]
128pub struct SystemClock;
129
130impl Clock for SystemClock {
131    fn now(&self) -> Timestamp {
132        SystemTime::now().into()
133    }
134}
135
136pub struct MockClock {
137    time: Timestamp,
138}
139
140impl MockClock {
141    pub fn new(time: Timestamp) -> Self {
142        Self { time }
143    }
144
145    pub fn set_time(&mut self, time: Timestamp) {
146        self.time = time;
147    }
148
149    pub fn advance(&mut self, duration: Duration) {
150        self.time = self.time + duration;
151    }
152}
153
154impl Clock for MockClock {
155    fn now(&self) -> Timestamp {
156        self.time
157    }
158}