appletheia_application/outbox/
outbox_attempt_count.rs1use std::{fmt, fmt::Display};
2
3use super::OutboxAttemptCountError;
4
5#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
6pub struct OutboxAttemptCount(i64);
7
8impl OutboxAttemptCount {
9 pub fn new() -> Self {
10 Self(0)
11 }
12
13 pub fn value(&self) -> i64 {
14 self.0
15 }
16
17 pub fn increment(self) -> Self {
18 Self(self.value() + 1)
19 }
20
21 pub fn checked_increment(self) -> Option<Self> {
22 self.value().checked_add(1).map(Self)
23 }
24
25 pub fn try_increment(self) -> Result<Self, OutboxAttemptCountError> {
26 self.checked_increment()
27 .ok_or(OutboxAttemptCountError::Overflow)
28 }
29}
30
31impl Default for OutboxAttemptCount {
32 fn default() -> Self {
33 Self::new()
34 }
35}
36
37impl TryFrom<i64> for OutboxAttemptCount {
38 type Error = OutboxAttemptCountError;
39
40 fn try_from(value: i64) -> Result<Self, Self::Error> {
41 if value < 0 {
42 Err(OutboxAttemptCountError::NegativeValue(value))
43 } else {
44 Ok(Self(value))
45 }
46 }
47}
48
49impl From<OutboxAttemptCount> for i64 {
50 fn from(value: OutboxAttemptCount) -> Self {
51 value.value()
52 }
53}
54
55impl Display for OutboxAttemptCount {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 write!(f, "{}", self.value())
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn new_starts_at_zero() {
67 let count = OutboxAttemptCount::new();
68 assert_eq!(count.value(), 0);
69 }
70
71 #[test]
72 fn try_from_accepts_non_negative_values() {
73 let count = OutboxAttemptCount::try_from(5).expect("expected valid count");
74 assert_eq!(count.value(), 5);
75 }
76
77 #[test]
78 fn try_from_rejects_negative_values() {
79 let err = OutboxAttemptCount::try_from(-1).expect_err("expected negative value error");
80 match err {
81 OutboxAttemptCountError::NegativeValue(v) => assert_eq!(v, -1),
82 _ => panic!("unexpected error variant"),
83 }
84 }
85
86 #[test]
87 fn increment_increments_count() {
88 let current = OutboxAttemptCount::new();
89 let next = current.increment();
90 assert_eq!(next.value(), 1);
91 }
92
93 #[test]
94 fn checked_increment_handles_overflow() {
95 let near_max = OutboxAttemptCount::try_from(i64::MAX - 1).unwrap();
96 let next = near_max
97 .checked_increment()
98 .expect("should provide next value");
99 assert_eq!(next.value(), i64::MAX);
100
101 let max = OutboxAttemptCount::try_from(i64::MAX).unwrap();
102 assert!(max.checked_increment().is_none());
103 }
104
105 #[test]
106 fn try_increment_returns_error_on_overflow() {
107 let near_max = OutboxAttemptCount::try_from(i64::MAX - 1).unwrap();
108 let next = near_max.try_increment().expect("should provide next value");
109 assert_eq!(next.value(), i64::MAX);
110
111 let max = OutboxAttemptCount::try_from(i64::MAX).unwrap();
112 let err = max.try_increment().expect_err("expected overflow error");
113 match err {
114 OutboxAttemptCountError::Overflow => {}
115 _ => panic!("unexpected error variant"),
116 }
117 }
118}