1#[derive(Debug, thiserror::Error)]
5pub enum IdError {
6 #[error("id must be non-zero (0 is reserved by the HFX format)")]
8 ZeroId,
9
10 #[error("id must be positive, got {value}")]
12 NegativeId {
13 value: i64,
15 },
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
23pub struct UnitId(i64);
24
25impl UnitId {
26 pub fn new(raw: i64) -> Result<Self, IdError> {
35 match raw {
36 0 => Err(IdError::ZeroId),
37 v if v < 0 => Err(IdError::NegativeId { value: raw }),
38 _ => Ok(Self(raw)),
39 }
40 }
41
42 pub fn get(self) -> i64 {
44 self.0
45 }
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
54pub struct SnapId(i64);
55
56impl SnapId {
57 pub fn new(raw: i64) -> Result<Self, IdError> {
66 match raw {
67 0 => Err(IdError::ZeroId),
68 v if v < 0 => Err(IdError::NegativeId { value: raw }),
69 _ => Ok(Self(raw)),
70 }
71 }
72
73 pub fn get(self) -> i64 {
75 self.0
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn unit_id_accepts_positive() {
85 let id = UnitId::new(1).unwrap();
86 assert_eq!(id.get(), 1);
87 }
88
89 #[test]
90 fn unit_id_rejects_zero() {
91 assert!(matches!(UnitId::new(0), Err(IdError::ZeroId)));
92 }
93
94 #[test]
95 fn unit_id_rejects_negative() {
96 assert!(matches!(
97 UnitId::new(-5),
98 Err(IdError::NegativeId { value: -5 })
99 ));
100 }
101
102 #[test]
103 fn snap_id_accepts_positive() {
104 let id = SnapId::new(42).unwrap();
105 assert_eq!(id.get(), 42);
106 }
107
108 #[test]
109 fn snap_id_rejects_zero() {
110 assert!(matches!(SnapId::new(0), Err(IdError::ZeroId)));
111 }
112
113 #[test]
114 fn snap_id_rejects_negative() {
115 assert!(matches!(
116 SnapId::new(-1),
117 Err(IdError::NegativeId { value: -1 })
118 ));
119 }
120
121 #[test]
122 fn unit_and_snap_are_distinct_types() {
123 let _u: UnitId = UnitId::new(10).unwrap();
127 let _s: SnapId = SnapId::new(10).unwrap();
128 }
129
130 #[test]
131 fn unit_id_max_value_succeeds() {
132 let id = UnitId::new(i64::MAX).unwrap();
133 assert_eq!(id.get(), i64::MAX);
134 }
135
136 #[test]
137 fn unit_id_min_value_fails_with_negative_id() {
138 assert!(matches!(
139 UnitId::new(i64::MIN),
140 Err(IdError::NegativeId { value: i64::MIN })
141 ));
142 }
143
144 #[test]
145 fn unit_id_equality() {
146 let a = UnitId::new(7).unwrap();
147 let b = UnitId::new(7).unwrap();
148 assert_eq!(a, b);
149 }
150
151 #[test]
152 fn unit_id_ordering() {
153 let a = UnitId::new(1).unwrap();
154 let b = UnitId::new(2).unwrap();
155 assert!(a < b);
156 }
157
158 #[test]
159 fn unit_id_usable_in_hash_set() {
160 use std::collections::HashSet;
161 let mut set = HashSet::new();
162 set.insert(UnitId::new(1).unwrap());
163 set.insert(UnitId::new(2).unwrap());
164 set.insert(UnitId::new(1).unwrap());
165 assert_eq!(set.len(), 2);
166 assert!(set.contains(&UnitId::new(1).unwrap()));
167 }
168}