made_core/value_objects/audit/
audit_sequence.rs1use serde::{Deserialize, Serialize};
2
3use crate::error::DomainError;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
11#[serde(transparent)]
12pub struct AuditSequence(u64);
13
14impl AuditSequence {
15 pub const FIRST: Self = Self(1);
16
17 pub fn new(value: u64) -> Result<Self, DomainError> {
18 if value == 0 {
19 return Err(DomainError::MustBeNonZero {
20 field: "audit_sequence",
21 });
22 }
23 Ok(Self(value))
24 }
25
26 #[must_use]
27 pub fn value(self) -> u64 {
28 self.0
29 }
30
31 #[must_use]
36 pub fn next(self) -> Self {
37 Self(self.0.saturating_add(1))
38 }
39
40 #[must_use]
41 pub fn follows(self, previous: Self) -> bool {
42 self.0 == previous.0.saturating_add(1)
43 }
44
45 #[must_use]
46 pub fn is_first(self) -> bool {
47 self == Self::FIRST
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 #[test]
56 fn zero_is_rejected() {
57 assert!(matches!(
58 AuditSequence::new(0),
59 Err(DomainError::MustBeNonZero {
60 field: "audit_sequence"
61 })
62 ));
63 }
64
65 #[test]
66 fn the_first_sequence_is_one() {
67 assert_eq!(AuditSequence::FIRST.value(), 1);
68 assert!(AuditSequence::FIRST.is_first());
69 }
70
71 #[test]
72 fn follows_only_accepts_the_immediate_successor() {
73 let first = AuditSequence::FIRST;
74
75 assert!(first.next().follows(first));
76 assert!(!AuditSequence::new(3).unwrap().follows(first));
77 assert!(!first.follows(first));
78 }
79}