1use crate::{CoreError, Epoch};
30
31pub const MAX_SEQ_KEY_LEN: usize = 128;
33
34pub const DEFAULT_MAX_SEQ_COUNT: u32 = 65_536;
48
49#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
60pub struct SeqKey(String);
61
62impl SeqKey {
63 pub fn try_new(s: impl Into<String>) -> Result<Self, CoreError> {
64 let s = s.into();
65 if s.is_empty() {
66 return Err(CoreError::SeqKeyEmpty);
67 }
68 if s.len() > MAX_SEQ_KEY_LEN {
69 return Err(CoreError::SeqKeyTooLong {
70 len: s.len(),
71 max: MAX_SEQ_KEY_LEN,
72 });
73 }
74 Ok(SeqKey(s))
75 }
76
77 pub fn as_str(&self) -> &str {
78 &self.0
79 }
80}
81
82#[cfg(feature = "serde")]
83impl serde::Serialize for SeqKey {
84 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
85 serializer.serialize_str(&self.0)
86 }
87}
88
89#[cfg(feature = "serde")]
90impl<'de> serde::Deserialize<'de> for SeqKey {
91 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
92 let s = String::deserialize(deserializer)?;
97 SeqKey::try_new(s).map_err(serde::de::Error::custom)
98 }
99}
100
101#[derive(Clone, Debug, PartialEq, Eq)]
112pub struct SeqGrant {
113 key: SeqKey,
114 start: u64,
115 count: u32,
116 epoch: Epoch,
117}
118
119impl SeqGrant {
120 pub fn try_new(key: SeqKey, start: u64, count: u32, epoch: Epoch) -> Result<Self, CoreError> {
130 if count == 0 {
131 return Err(CoreError::SeqCountZero);
132 }
133 if start.checked_add(u64::from(count) - 1).is_none() {
135 return Err(CoreError::SeqBlockOverflow { start, count });
136 }
137 Ok(SeqGrant {
138 key,
139 start,
140 count,
141 epoch,
142 })
143 }
144 pub fn key(&self) -> &SeqKey {
145 &self.key
146 }
147 pub fn start(&self) -> u64 {
148 self.start
149 }
150 pub fn count(&self) -> u32 {
151 self.count
152 }
153 pub fn epoch(&self) -> Epoch {
154 self.epoch
155 }
156 pub fn last(&self) -> u64 {
164 self.start + (u64::from(self.count) - 1)
165 }
166}
167
168#[derive(Debug)]
169enum SeqState {
170 NotLeader,
171 Leader { epoch: Epoch },
172}
173
174pub struct SeqAllocator {
179 state: SeqState,
180}
181
182impl SeqAllocator {
183 pub fn new() -> Self {
184 SeqAllocator {
185 state: SeqState::NotLeader,
186 }
187 }
188
189 pub fn become_leader(&mut self, epoch: Epoch) {
192 self.state = SeqState::Leader { epoch };
193 }
194
195 pub fn step_down(&mut self) {
196 self.state = SeqState::NotLeader;
197 }
198
199 pub fn is_leader(&self) -> bool {
200 matches!(self.state, SeqState::Leader { .. })
201 }
202
203 pub fn epoch(&self) -> Option<Epoch> {
204 match self.state {
205 SeqState::Leader { epoch } => Some(epoch),
206 SeqState::NotLeader => None,
207 }
208 }
209
210 pub fn validate_request(
222 &self,
223 key: &str,
224 count: u32,
225 max_count: u32,
226 ) -> Result<SeqKey, CoreError> {
227 if !self.is_leader() {
228 return Err(CoreError::NotLeader);
229 }
230 if count == 0 {
231 return Err(CoreError::SeqCountZero);
232 }
233 if count > max_count {
234 return Err(CoreError::SeqCountTooLarge {
235 count,
236 max: max_count,
237 });
238 }
239 SeqKey::try_new(key)
240 }
241}
242
243impl Default for SeqAllocator {
244 fn default() -> Self {
245 Self::new()
246 }
247}
248
249#[cfg(test)]
250mod seqallocator_tests {
251 use super::*;
252
253 #[test]
254 fn new_is_not_leader() {
255 let a = SeqAllocator::new();
256 assert!(!a.is_leader());
257 assert_eq!(a.epoch(), None);
258 }
259
260 #[test]
261 fn become_leader_then_step_down() {
262 let mut a = SeqAllocator::new();
263 a.become_leader(Epoch(3));
264 assert!(a.is_leader());
265 assert_eq!(a.epoch(), Some(Epoch(3)));
266 a.step_down();
267 assert!(!a.is_leader());
268 assert_eq!(a.epoch(), None);
269 }
270
271 #[test]
272 fn validate_request_off_leader_is_not_leader() {
273 let a = SeqAllocator::new();
274 assert_eq!(
275 a.validate_request("orders", 1, DEFAULT_MAX_SEQ_COUNT),
276 Err(CoreError::NotLeader)
277 );
278 }
279
280 #[test]
281 fn validate_request_rejects_zero_count() {
282 let mut a = SeqAllocator::new();
283 a.become_leader(Epoch(1));
284 assert_eq!(
285 a.validate_request("orders", 0, DEFAULT_MAX_SEQ_COUNT),
286 Err(CoreError::SeqCountZero)
287 );
288 }
289
290 #[test]
291 fn validate_request_rejects_oversized_count() {
292 let mut a = SeqAllocator::new();
293 a.become_leader(Epoch(1));
294 assert_eq!(
295 a.validate_request("orders", DEFAULT_MAX_SEQ_COUNT + 1, DEFAULT_MAX_SEQ_COUNT),
296 Err(CoreError::SeqCountTooLarge {
297 count: DEFAULT_MAX_SEQ_COUNT + 1,
298 max: DEFAULT_MAX_SEQ_COUNT
299 })
300 );
301 }
302
303 #[test]
304 fn validate_request_uses_caller_provided_max() {
305 let mut a = SeqAllocator::new();
309 a.become_leader(Epoch(1));
310 assert_eq!(
311 a.validate_request("orders", 11, 10),
312 Err(CoreError::SeqCountTooLarge { count: 11, max: 10 }),
313 "count above a small configured cap must be rejected",
314 );
315 assert!(
316 a.validate_request("orders", 10, 10).is_ok(),
317 "count at the configured cap must be accepted",
318 );
319 assert!(
320 a.validate_request(
321 "orders",
322 DEFAULT_MAX_SEQ_COUNT + 1,
323 DEFAULT_MAX_SEQ_COUNT * 2
324 )
325 .is_ok(),
326 "a larger configured cap must accept counts above the default",
327 );
328 }
329
330 #[test]
331 fn validate_request_rejects_bad_key() {
332 let mut a = SeqAllocator::new();
333 a.become_leader(Epoch(1));
334 assert_eq!(
335 a.validate_request("", 1, DEFAULT_MAX_SEQ_COUNT),
336 Err(CoreError::SeqKeyEmpty)
337 );
338 }
339
340 #[test]
341 fn validate_request_ok_returns_key() {
342 let mut a = SeqAllocator::new();
343 a.become_leader(Epoch(1));
344 let k = a
345 .validate_request("orders", 10, DEFAULT_MAX_SEQ_COUNT)
346 .unwrap();
347 assert_eq!(k.as_str(), "orders");
348 }
349
350 #[test]
351 fn validate_request_accepts_max_count_exactly() {
352 let mut a = SeqAllocator::new();
353 a.become_leader(Epoch(1));
354 assert!(
355 a.validate_request("orders", DEFAULT_MAX_SEQ_COUNT, DEFAULT_MAX_SEQ_COUNT)
356 .is_ok()
357 );
358 }
359}
360
361#[cfg(test)]
362mod seqgrant_tests {
363 use super::*;
364
365 #[test]
366 fn exposes_fields_and_last() {
367 let key = SeqKey::try_new("users").unwrap();
368 let g = SeqGrant::try_new(key.clone(), 100, 5, Epoch(7)).unwrap();
369 assert_eq!(g.key().as_str(), "users");
370 assert_eq!(g.start(), 100);
371 assert_eq!(g.count(), 5);
372 assert_eq!(g.epoch(), Epoch(7));
373 assert_eq!(g.last(), 104);
375 }
376
377 #[test]
378 fn last_equals_start_when_count_is_one() {
379 let g1 = SeqGrant::try_new(SeqKey::try_new("x").unwrap(), 42, 1, Epoch(1)).unwrap();
380 assert_eq!(g1.last(), 42);
381 }
382
383 #[test]
384 fn try_new_rejects_zero_count() {
385 let key = SeqKey::try_new("x").unwrap();
386 assert_eq!(
387 SeqGrant::try_new(key, 5, 0, Epoch(1)),
388 Err(CoreError::SeqCountZero)
389 );
390 }
391
392 #[test]
393 fn try_new_rejects_block_overflow() {
394 let key = SeqKey::try_new("x").unwrap();
396 assert_eq!(
397 SeqGrant::try_new(key, u64::MAX, 2, Epoch(1)),
398 Err(CoreError::SeqBlockOverflow {
399 start: u64::MAX,
400 count: 2
401 })
402 );
403 }
404
405 #[test]
406 fn try_new_accepts_max_boundary() {
407 let key = SeqKey::try_new("x").unwrap();
409 let g = SeqGrant::try_new(key, u64::MAX - 4, 5, Epoch(1)).unwrap();
410 assert_eq!(g.last(), u64::MAX);
411 }
412}
413
414#[cfg(test)]
415mod seqkey_tests {
416 use super::*;
417
418 #[test]
419 fn accepts_normal_key() {
420 let k = SeqKey::try_new("orders").unwrap();
421 assert_eq!(k.as_str(), "orders");
422 }
423
424 #[test]
425 fn rejects_empty() {
426 assert_eq!(SeqKey::try_new(""), Err(CoreError::SeqKeyEmpty));
427 }
428
429 #[test]
430 fn accepts_max_length() {
431 let s = "a".repeat(MAX_SEQ_KEY_LEN);
432 assert!(SeqKey::try_new(&s).is_ok());
433 }
434
435 #[test]
436 fn rejects_one_past_max_length() {
437 let s = "a".repeat(MAX_SEQ_KEY_LEN + 1);
438 assert_eq!(
439 SeqKey::try_new(&s),
440 Err(CoreError::SeqKeyTooLong {
441 len: MAX_SEQ_KEY_LEN + 1,
442 max: MAX_SEQ_KEY_LEN
443 })
444 );
445 }
446
447 #[test]
448 fn length_is_measured_in_utf8_bytes_not_chars() {
449 let ok = "é".repeat(MAX_SEQ_KEY_LEN / 2);
451 assert!(SeqKey::try_new(&ok).is_ok());
452 let too_long = "é".repeat(MAX_SEQ_KEY_LEN / 2 + 1);
453 assert!(matches!(
454 SeqKey::try_new(&too_long),
455 Err(CoreError::SeqKeyTooLong { .. })
456 ));
457 }
458
459 #[cfg(feature = "serde")]
466 #[test]
467 fn serde_deserialize_rejects_empty_key() {
468 let bytes = postcard::to_stdvec(&String::new()).expect("encode empty string");
472 let decoded = postcard::from_bytes::<SeqKey>(&bytes);
473 assert!(
474 decoded.is_err(),
475 "empty-key postcard payload must fail to decode, got {decoded:?}",
476 );
477 }
478
479 #[cfg(feature = "serde")]
480 #[test]
481 fn serde_deserialize_rejects_oversized_key() {
482 let oversized = "a".repeat(MAX_SEQ_KEY_LEN + 1);
483 let bytes = postcard::to_stdvec(&oversized).expect("encode oversized string");
484 let decoded = postcard::from_bytes::<SeqKey>(&bytes);
485 assert!(
486 decoded.is_err(),
487 "oversized-key postcard payload must fail to decode, got {decoded:?}",
488 );
489 }
490
491 #[cfg(feature = "serde")]
492 #[test]
493 fn serde_round_trip_preserves_valid_key() {
494 let key = SeqKey::try_new("orders").unwrap();
495 let bytes = postcard::to_stdvec(&key).expect("encode key");
496 assert_eq!(bytes, postcard::to_stdvec(&"orders".to_string()).unwrap());
500 let back: SeqKey = postcard::from_bytes(&bytes).expect("decode key");
501 assert_eq!(back, key);
502 assert_eq!(back.as_str(), "orders");
503 }
504
505 #[cfg(feature = "serde")]
506 #[test]
507 fn serde_deserialize_accepts_max_length_key() {
508 let max = "a".repeat(MAX_SEQ_KEY_LEN);
509 let bytes = postcard::to_stdvec(&max).expect("encode max-length string");
510 let back: SeqKey = postcard::from_bytes(&bytes).expect("max-length key must decode");
511 assert_eq!(back.as_str(), max);
512 }
513}