Skip to main content

seedlink_rs_protocol/
sequence.rs

1use crate::error::{Result, SeedlinkError};
2
3#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
4pub struct SequenceNumber(u64);
5
6impl SequenceNumber {
7    /// Sentinel: sequence not set.
8    pub const UNSET: Self = Self(u64::MAX);
9
10    /// Sentinel: request all data (v4).
11    pub const ALL_DATA: Self = Self(u64::MAX - 1);
12
13    /// Maximum sequence value for v3 (6 hex digits).
14    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    /// Returns true if this is a special sentinel value (UNSET or ALL_DATA).
25    pub fn is_special(self) -> bool {
26        self == Self::UNSET || self == Self::ALL_DATA
27    }
28
29    /// Parse v3 hex representation (6 uppercase hex digits, e.g. "00001A").
30    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    /// Serialize to v3 hex (6 uppercase hex digits).
43    pub fn to_v3_hex(self) -> String {
44        format!("{:06X}", self.0)
45    }
46
47    /// Parse v4 decimal string (e.g. "26").
48    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    /// Serialize to v4 decimal string.
56    pub fn to_v4_decimal(self) -> String {
57        self.0.to_string()
58    }
59
60    /// Parse from v4 little-endian bytes (frame header).
61    pub fn from_v4_le_bytes(bytes: [u8; 8]) -> Self {
62        Self(u64::from_le_bytes(bytes))
63    }
64
65    /// Serialize to v4 little-endian bytes (frame header).
66    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}