1use std::{fmt, str::FromStr};
5
6use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
7
8use crate::{Object, ParseError};
9
10#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct DeviceId(String);
13
14impl DeviceId {
15 pub fn as_str(&self) -> &str {
17 &self.0
18 }
19}
20
21impl FromStr for DeviceId {
22 type Err = ParseError;
23
24 fn from_str(s: &str) -> Result<Self, Self::Err> {
25 let valid =
26 (1..=64).contains(&s.len()) && s.bytes().all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'-');
27 if valid { Ok(DeviceId(s.to_owned())) } else { Err(ParseError::Device(s.to_owned())) }
28 }
29}
30
31impl fmt::Display for DeviceId {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 f.write_str(&self.0)
34 }
35}
36
37impl Serialize for DeviceId {
38 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
39 serializer.serialize_str(&self.0)
40 }
41}
42
43impl<'de> Deserialize<'de> for DeviceId {
44 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
45 String::deserialize(deserializer)?.parse().map_err(de::Error::custom)
46 }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
55pub struct Hlc {
56 #[serde(deserialize_with = "crate::safe_u64")]
58 pub wall_ms: u64,
59 pub counter: u32,
61 pub device: DeviceId,
63}
64
65impl Object for Hlc {
66 const TYPE: &'static str = "timestamp";
67}
68
69#[derive(Debug, Clone)]
71pub struct Clock {
72 device: DeviceId,
73 wall_ms: u64,
74 counter: u32,
75}
76
77impl Clock {
78 pub fn new(device: DeviceId) -> Self {
80 Clock { device, wall_ms: 0, counter: 0 }
81 }
82
83 pub fn now(&mut self, physical_ms: u64) -> Hlc {
85 if physical_ms > self.wall_ms {
86 self.wall_ms = physical_ms;
87 self.counter = 0;
88 } else {
89 self.counter += 1;
90 }
91 self.stamp()
92 }
93
94 pub fn observe(&mut self, seen: &Hlc, physical_ms: u64) {
96 let wall_ms = self.wall_ms.max(seen.wall_ms).max(physical_ms);
97 self.counter = match (wall_ms == self.wall_ms, wall_ms == seen.wall_ms) {
98 (true, true) => self.counter.max(seen.counter),
99 (true, false) => self.counter,
100 (false, true) => seen.counter,
101 (false, false) => 0,
102 };
103 self.wall_ms = wall_ms;
104 }
105
106 fn stamp(&self) -> Hlc {
107 Hlc { wall_ms: self.wall_ms, counter: self.counter, device: self.device.clone() }
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 fn device(name: &str) -> DeviceId {
116 name.parse().unwrap()
117 }
118
119 #[test]
120 fn device_ids() {
121 assert!("laptop-2".parse::<DeviceId>().is_ok());
122 for s in ["", "Laptop", "my laptop", &"x".repeat(65)] {
123 assert!(s.parse::<DeviceId>().is_err(), "{s:?}");
124 }
125 }
126
127 #[test]
128 fn local_timestamps_always_increase() {
129 let mut clock = Clock::new(device("a"));
130 let first = clock.now(1000);
131 let same_ms = clock.now(1000);
132 let clock_went_back = clock.now(500);
133 assert!(first < same_ms && same_ms < clock_went_back);
134 assert_eq!(clock_went_back.wall_ms, 1000);
135 }
136
137 #[test]
138 fn after_observing_a_later_timestamp_the_next_one_is_later_still() {
139 let mut ahead = Clock::new(device("ahead"));
140 let mut behind = Clock::new(device("behind"));
141 let remote = ahead.now(5000);
142 behind.observe(&remote, 1000);
143 let local = behind.now(1000);
144 assert!(local > remote);
145 assert_eq!(local.wall_ms, 5000);
146 }
147
148 #[test]
149 fn ties_are_broken_by_device() {
150 let a = Clock::new(device("a")).now(1000);
151 let b = Clock::new(device("b")).now(1000);
152 assert!(a < b);
153 }
154}