1use crate::MergeError;
2use coven_foundation::id_source::DeviceId;
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub struct Timestamp {
8 milliseconds: u64,
9 counter: u16,
10 device: DeviceId,
11}
12
13impl Timestamp {
14 pub const MAX_MILLISECONDS: u64 = (1_u64 << 48) - 1;
16
17 pub fn new(milliseconds: u64, counter: u16, device: DeviceId) -> Result<Self, MergeError> {
19 if milliseconds > Self::MAX_MILLISECONDS {
20 return Err(MergeError::MillisecondsOutOfRange(milliseconds));
21 }
22 Ok(Self { milliseconds, counter, device })
23 }
24
25 pub fn next(latest: Option<Self>, clock_ms: u64, device: DeviceId) -> Result<Self, MergeError> {
29 if clock_ms > Self::MAX_MILLISECONDS {
30 return Err(MergeError::MillisecondsOutOfRange(clock_ms));
31 }
32 match latest {
33 None => Self::new(clock_ms, 0, device),
34 Some(last) if clock_ms > last.milliseconds => Self::new(clock_ms, 0, device),
35 Some(last) => match last.counter.checked_add(1) {
36 Some(counter) => Self::new(last.milliseconds, counter, device),
37 None if last.milliseconds < Self::MAX_MILLISECONDS => {
38 Self::new(last.milliseconds + 1, 0, device)
39 }
40 None => Err(MergeError::TimestampExhausted),
41 },
42 }
43 }
44
45 pub fn milliseconds(self) -> u64 {
47 self.milliseconds
48 }
49
50 pub fn counter(self) -> u16 {
52 self.counter
53 }
54
55 pub fn device(self) -> DeviceId {
57 self.device
58 }
59}
60
61#[cfg(test)]
62#[path = "timestamp_tests.rs"]
63mod tests;