kalamdb_commons/ids/
seq_id.rs1use std::{
7 fmt,
8 time::{SystemTime, UNIX_EPOCH},
9};
10
11use serde::{
12 de::{self, Visitor},
13 Deserialize, Deserializer, Serialize, Serializer,
14};
15
16use crate::{ids::SnowflakeGenerator, StorageKey};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
28pub struct SeqId(i64);
29
30impl SeqId {
31 pub const EPOCH: u64 = 1704067200000;
33
34 pub fn new(snowflake_id: i64) -> Self {
36 Self(snowflake_id)
37 }
38
39 pub fn from_i64(value: i64) -> Self {
41 Self(value)
42 }
43
44 pub fn as_i64(&self) -> i64 {
46 self.0
47 }
48
49 pub fn timestamp_millis(&self) -> u64 {
53 let id = self.0 as u64;
54 (id >> 22) + Self::EPOCH
55 }
56
57 pub fn timestamp_seconds(&self) -> u64 {
59 self.timestamp_millis() / 1000
60 }
61
62 pub fn age_seconds(&self, now_millis: u64) -> u64 {
64 let ts = self.timestamp_millis();
65 now_millis.saturating_sub(ts) / 1000
66 }
67
68 pub fn timestamp(&self) -> SystemTime {
70 let millis = self.timestamp_millis();
71 UNIX_EPOCH + std::time::Duration::from_millis(millis)
72 }
73
74 pub fn worker_id(&self) -> u16 {
76 let id = self.0 as u64;
77 ((id >> 12) & 0x3FF) as u16
78 }
79
80 pub fn sequence(&self) -> u16 {
82 let id = self.0 as u64;
83 (id & 0xFFF) as u16
84 }
85
86 pub fn from_string(s: &str) -> Result<Self, String> {
90 s.parse::<i64>()
91 .map(Self::new)
92 .map_err(|e| format!("Failed to parse SeqId: {}", e))
93 }
94
95 pub fn to_bytes(&self) -> [u8; 8] {
97 self.0.to_be_bytes()
98 }
99
100 pub fn from_bytes(bytes: &[u8]) -> Result<Self, String> {
102 if bytes.len() != 8 {
103 return Err(format!("Invalid byte length: expected 8, got {}", bytes.len()));
104 }
105 let mut array = [0u8; 8];
106 array.copy_from_slice(bytes);
107 Ok(Self::new(i64::from_be_bytes(array)))
108 }
109
110 pub fn max_id_for_timestamp(timestamp_millis: u64) -> Result<Self, String> {
116 let id = SnowflakeGenerator::max_id_for_timestamp(timestamp_millis)?;
118 Ok(Self::new(id))
119 }
120}
121
122impl fmt::Display for SeqId {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 write!(f, "{}", self.0)
125 }
126}
127
128impl Serialize for SeqId {
129 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
130 where
131 S: Serializer,
132 {
133 serializer.serialize_i64(self.0)
134 }
135}
136
137impl<'de> Deserialize<'de> for SeqId {
138 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
139 where
140 D: Deserializer<'de>,
141 {
142 struct SeqIdVisitor;
143
144 impl Visitor<'_> for SeqIdVisitor {
145 type Value = SeqId;
146
147 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
148 formatter.write_str("an i64 sequence ID or a decimal string")
149 }
150
151 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
152 where
153 E: de::Error,
154 {
155 Ok(SeqId::new(value))
156 }
157
158 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
159 where
160 E: de::Error,
161 {
162 let value = i64::try_from(value)
163 .map_err(|_| E::custom(format!("sequence ID {} exceeds i64 range", value)))?;
164 Ok(SeqId::new(value))
165 }
166
167 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
168 where
169 E: de::Error,
170 {
171 SeqId::from_string(value).map_err(E::custom)
172 }
173
174 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
175 where
176 E: de::Error,
177 {
178 self.visit_str(&value)
179 }
180 }
181
182 deserializer.deserialize_any(SeqIdVisitor)
183 }
184}
185
186impl From<i64> for SeqId {
187 fn from(value: i64) -> Self {
188 Self::new(value)
189 }
190}
191
192impl From<SeqId> for i64 {
193 fn from(seq_id: SeqId) -> Self {
194 seq_id.0
195 }
196}
197
198impl StorageKey for SeqId {
199 fn storage_key(&self) -> Vec<u8> {
200 self.to_bytes().to_vec()
201 }
202
203 fn from_storage_key(bytes: &[u8]) -> Result<Self, String> {
204 Self::from_bytes(bytes)
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn test_seq_id_creation() {
214 let seq_id = SeqId::new(123456789);
215 assert_eq!(seq_id.as_i64(), 123456789);
216 }
217
218 #[test]
219 fn test_seq_id_timestamp_extraction() {
220 let timestamp_offset = 1000u64; let worker_id = 5u64;
223 let sequence = 42u64;
224
225 let id = (timestamp_offset << 22) | (worker_id << 12) | sequence;
226 let seq_id = SeqId::new(id as i64);
227
228 assert_eq!(seq_id.timestamp_millis(), SeqId::EPOCH + timestamp_offset);
229 assert_eq!(seq_id.worker_id(), 5);
230 assert_eq!(seq_id.sequence(), 42);
231 }
232
233 #[test]
234 fn test_seq_id_timestamp_seconds() {
235 let timestamp_offset = 5000u64; let id = (timestamp_offset << 22) as i64;
237 let seq_id = SeqId::new(id);
238
239 assert_eq!(seq_id.timestamp_seconds(), (SeqId::EPOCH + timestamp_offset) / 1000);
240 }
241
242 #[test]
243 fn test_seq_id_age_seconds() {
244 let timestamp_offset = 2000u64;
245 let id = (timestamp_offset << 22) as i64;
246 let seq_id = SeqId::new(id);
247 let now_millis = SeqId::EPOCH + timestamp_offset + 7000; assert_eq!(seq_id.age_seconds(now_millis), 7);
250 }
251
252 #[test]
253 fn test_seq_id_string_conversion() {
254 let seq_id = SeqId::new(987654321);
255 let s = seq_id.to_string();
256 assert_eq!(s, "987654321");
257
258 let parsed = SeqId::from_string(&s).unwrap();
259 assert_eq!(parsed, seq_id);
260 }
261
262 #[test]
263 fn test_seq_id_bytes_conversion() {
264 let seq_id = SeqId::new(123456789);
265 let bytes = seq_id.to_bytes();
266 let parsed = SeqId::from_bytes(&bytes).unwrap();
267 assert_eq!(parsed, seq_id);
268 }
269
270 #[test]
271 fn test_seq_id_max_id_for_timestamp() {
272 let ts = SeqId::EPOCH + 5000;
273 let seq_id = SeqId::max_id_for_timestamp(ts).expect("seq id");
274 assert!(seq_id.timestamp_millis() >= ts);
275 assert!(seq_id >= SeqId::new(0));
276 }
277
278 #[test]
279 fn test_seq_id_ordering() {
280 let seq1 = SeqId::new(100);
281 let seq2 = SeqId::new(200);
282 let seq3 = SeqId::new(300);
283
284 assert!(seq1 < seq2);
285 assert!(seq2 < seq3);
286 assert!(seq3 > seq1);
287 }
288
289 #[test]
290 fn test_seq_id_from_i64() {
291 let seq_id: SeqId = 42i64.into();
292 assert_eq!(seq_id.as_i64(), 42);
293
294 let value: i64 = seq_id.into();
295 assert_eq!(value, 42);
296 }
297}