walnut_dns/rr/
sequence.rs1use std::{cmp::Ordering, ops::Add};
2
3use rusqlite::{ToSql, types::FromSql};
4
5#[derive(Debug, Clone, Copy, PartialEq)]
10pub struct SerialNumber(u32);
11
12impl SerialNumber {
13 pub const ZERO: SerialNumber = SerialNumber(0);
14
15 pub fn get(&self) -> u32 {
23 self.0
24 }
25}
26
27impl From<u32> for SerialNumber {
28 fn from(value: u32) -> Self {
29 SerialNumber(value)
30 }
31}
32
33impl Add for SerialNumber {
37 type Output = Self;
38
39 fn add(self, rhs: Self) -> Self::Output {
40 Self(self.0.wrapping_add(rhs.0))
41 }
42}
43
44impl Add<u32> for SerialNumber {
45 type Output = Self;
46
47 fn add(self, rhs: u32) -> Self::Output {
48 Self(self.0.wrapping_add(rhs))
49 }
50}
51
52impl PartialOrd for SerialNumber {
54 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
55 const SERIAL_BITS_HALF: u32 = 1 << (u32::BITS - 1);
56
57 let i1 = self.0;
58 let i2 = other.0;
59
60 if i1 == i2 {
61 Some(Ordering::Equal)
62 } else if (i1 < i2 && (i2 - i1) < SERIAL_BITS_HALF)
63 || (i1 > i2 && (i1 - i2) > SERIAL_BITS_HALF)
64 {
65 Some(Ordering::Less)
66 } else if (i1 < i2 && (i2 - i1) > SERIAL_BITS_HALF)
67 || (i1 > i2 && (i1 - i2) < SERIAL_BITS_HALF)
68 {
69 Some(Ordering::Greater)
70 } else {
71 None
72 }
73 }
74}
75
76impl ToSql for SerialNumber {
77 fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
78 self.0.to_sql()
79 }
80}
81
82impl FromSql for SerialNumber {
83 fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
84 match value {
85 rusqlite::types::ValueRef::Integer(i) => Ok(SerialNumber(i as u32)),
86 _ => Err(rusqlite::types::FromSqlError::InvalidType),
87 }
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_serial_number_creation() {
97 let sn = SerialNumber::from(123);
98 assert_eq!(sn.get(), 123);
99
100 let zero = SerialNumber::ZERO;
101 assert_eq!(zero.get(), 0);
102 }
103
104 #[test]
105 fn test_serial_number_addition() {
106 let sn1 = SerialNumber::from(10);
107 let sn2 = SerialNumber::from(20);
108 let result = sn1 + sn2;
109 assert_eq!(result.get(), 30);
110 }
111
112 #[test]
113 fn test_serial_number_wrapping_addition() {
114 let sn1 = SerialNumber::from(u32::MAX);
115 let sn2 = SerialNumber::from(1);
116 let result = sn1 + sn2;
117 assert_eq!(result.get(), 0); }
119
120 #[test]
121 fn test_serial_number_equality() {
122 let sn1 = SerialNumber::from(42);
123 let sn2 = SerialNumber::from(42);
124 let sn3 = SerialNumber::from(43);
125
126 assert_eq!(sn1, sn2);
127 assert_ne!(sn1, sn3);
128 assert_eq!(sn1.partial_cmp(&sn2), Some(Ordering::Equal));
129 }
130
131 #[test]
132 fn test_serial_number_comparison_simple() {
133 let sn1 = SerialNumber::from(10);
134 let sn2 = SerialNumber::from(20);
135
136 assert_eq!(sn1.partial_cmp(&sn2), Some(Ordering::Less));
137 assert_eq!(sn2.partial_cmp(&sn1), Some(Ordering::Greater));
138 }
139
140 #[test]
141 fn test_serial_number_comparison_wrap_around() {
142 let sn1 = SerialNumber::from(100);
144 let sn2 = SerialNumber::from(100 + 0x40000000); let result = sn1.partial_cmp(&sn2);
148 assert_eq!(result, Some(std::cmp::Ordering::Less));
149
150 let sn3 = SerialNumber::from(0);
152 let sn4 = SerialNumber::from(0x80000000); let result_undefined = sn3.partial_cmp(&sn4);
154 assert_eq!(result_undefined, None); }
156
157 #[test]
158 fn test_serial_number_comparison_undefined() {
159 let sn1 = SerialNumber::from(0);
161 let sn2 = SerialNumber::from(0x80000000); assert_eq!(sn1.partial_cmp(&sn2), None);
165 }
166
167 #[test]
168 fn test_serial_number_rfc_examples() {
169 let s = SerialNumber::from(10);
171 let s_plus_1 = SerialNumber::from(11);
172 let s_plus_100 = SerialNumber::from(110);
173
174 assert_eq!(s.partial_cmp(&s_plus_1), Some(Ordering::Less));
175 assert_eq!(s.partial_cmp(&s_plus_100), Some(Ordering::Less));
176 assert_eq!(s_plus_1.partial_cmp(&s_plus_100), Some(Ordering::Less));
177 }
178
179 #[test]
180 fn test_serial_number_clone_and_copy() {
181 let sn1 = SerialNumber::from(42);
182 let sn2 = sn1; let sn3 = sn1; assert_eq!(sn1, sn2);
186 assert_eq!(sn1, sn3);
187 assert_eq!(sn1.get(), 42);
188 assert_eq!(sn2.get(), 42);
189 assert_eq!(sn3.get(), 42);
190 }
191}