seedlink_rs_protocol/
sequence.rs1use crate::error::{Result, SeedlinkError};
2
3#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
4pub struct SequenceNumber(u64);
5
6impl SequenceNumber {
7 pub const UNSET: Self = Self(u64::MAX);
9
10 pub const ALL_DATA: Self = Self(u64::MAX - 1);
12
13 pub const V3_MAX: u64 = 0xFF_FFFF;
15
16 pub fn new(value: u64) -> Self {
17 Self(value)
18 }
19
20 pub fn value(self) -> u64 {
21 self.0
22 }
23
24 pub fn is_special(self) -> bool {
26 self == Self::UNSET || self == Self::ALL_DATA
27 }
28
29 pub fn from_v3_hex(hex: &str) -> Result<Self> {
31 if hex.len() != 6 {
32 return Err(SeedlinkError::InvalidSequence(format!(
33 "v3 hex must be 6 chars, got {} ({hex:?})",
34 hex.len()
35 )));
36 }
37 let value = u64::from_str_radix(hex, 16)
38 .map_err(|_| SeedlinkError::InvalidSequence(format!("invalid v3 hex: {hex:?}")))?;
39 Ok(Self(value))
40 }
41
42 pub fn to_v3_hex(self) -> String {
44 format!("{:06X}", self.0)
45 }
46
47 pub fn from_v4_decimal(s: &str) -> Result<Self> {
49 let value: u64 = s
50 .parse()
51 .map_err(|_| SeedlinkError::InvalidSequence(format!("invalid v4 decimal: {s:?}")))?;
52 Ok(Self(value))
53 }
54
55 pub fn to_v4_decimal(self) -> String {
57 self.0.to_string()
58 }
59
60 pub fn from_v4_le_bytes(bytes: [u8; 8]) -> Self {
62 Self(u64::from_le_bytes(bytes))
63 }
64
65 pub fn to_v4_le_bytes(self) -> [u8; 8] {
67 self.0.to_le_bytes()
68 }
69}
70
71impl PartialOrd for SequenceNumber {
72 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
73 Some(self.cmp(other))
74 }
75}
76
77impl Ord for SequenceNumber {
78 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
79 self.0.cmp(&other.0)
80 }
81}
82
83impl std::fmt::Display for SequenceNumber {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 if *self == Self::UNSET {
86 write!(f, "UNSET")
87 } else if *self == Self::ALL_DATA {
88 write!(f, "ALL_DATA")
89 } else {
90 write!(f, "{}", self.0)
91 }
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn v3_hex_valid() {
101 let seq = SequenceNumber::from_v3_hex("00001A").unwrap();
102 assert_eq!(seq.value(), 26);
103 assert_eq!(seq.to_v3_hex(), "00001A");
104 }
105
106 #[test]
107 fn v3_hex_boundary_zero() {
108 let seq = SequenceNumber::from_v3_hex("000000").unwrap();
109 assert_eq!(seq.value(), 0);
110 assert_eq!(seq.to_v3_hex(), "000000");
111 }
112
113 #[test]
114 fn v3_hex_boundary_max() {
115 let seq = SequenceNumber::from_v3_hex("FFFFFF").unwrap();
116 assert_eq!(seq.value(), 0xFFFFFF);
117 assert_eq!(seq.to_v3_hex(), "FFFFFF");
118 }
119
120 #[test]
121 fn v3_hex_lowercase_accepted() {
122 let seq = SequenceNumber::from_v3_hex("00001a").unwrap();
123 assert_eq!(seq.value(), 26);
124 }
125
126 #[test]
127 fn v3_hex_invalid_chars() {
128 assert!(SequenceNumber::from_v3_hex("ZZZZZZ").is_err());
129 }
130
131 #[test]
132 fn v3_hex_wrong_length() {
133 assert!(SequenceNumber::from_v3_hex("001A").is_err());
134 assert!(SequenceNumber::from_v3_hex("0000001A").is_err());
135 }
136
137 #[test]
138 fn v3_hex_roundtrip() {
139 for val in [0u64, 1, 255, 0xFFFFFF] {
140 let seq = SequenceNumber::new(val);
141 let hex = seq.to_v3_hex();
142 let parsed = SequenceNumber::from_v3_hex(&hex).unwrap();
143 assert_eq!(parsed, seq);
144 }
145 }
146
147 #[test]
148 fn v4_decimal_valid() {
149 let seq = SequenceNumber::from_v4_decimal("26").unwrap();
150 assert_eq!(seq.value(), 26);
151 assert_eq!(seq.to_v4_decimal(), "26");
152 }
153
154 #[test]
155 fn v4_decimal_zero() {
156 let seq = SequenceNumber::from_v4_decimal("0").unwrap();
157 assert_eq!(seq.value(), 0);
158 }
159
160 #[test]
161 fn v4_decimal_invalid() {
162 assert!(SequenceNumber::from_v4_decimal("abc").is_err());
163 assert!(SequenceNumber::from_v4_decimal("-1").is_err());
164 }
165
166 #[test]
167 fn v4_le_bytes_roundtrip() {
168 let seq = SequenceNumber::new(0x0102030405060708);
169 let bytes = seq.to_v4_le_bytes();
170 assert_eq!(SequenceNumber::from_v4_le_bytes(bytes), seq);
171 }
172
173 #[test]
174 fn ordering() {
175 let a = SequenceNumber::new(10);
176 let b = SequenceNumber::new(20);
177 assert!(a < b);
178 assert!(b > a);
179 }
180
181 #[test]
182 fn special_values() {
183 assert!(SequenceNumber::UNSET.is_special());
184 assert!(SequenceNumber::ALL_DATA.is_special());
185 assert!(!SequenceNumber::new(0).is_special());
186 assert!(!SequenceNumber::new(42).is_special());
187 }
188
189 #[test]
190 fn display_special() {
191 assert_eq!(SequenceNumber::UNSET.to_string(), "UNSET");
192 assert_eq!(SequenceNumber::ALL_DATA.to_string(), "ALL_DATA");
193 assert_eq!(SequenceNumber::new(42).to_string(), "42");
194 }
195}