1use bytes::{BufMut, Bytes, BytesMut};
31
32use super::DeserializeError;
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub struct KeyPrefix {
59 subsystem: u8,
60 version: u8,
61 tag: u8,
62}
63
64impl KeyPrefix {
65 pub fn new(subsystem: u8, version: u8, tag: u8) -> Self {
71 assert!(subsystem > 0, "subsystem 0 is reserved");
72 assert!(version > 0, "key version 0 is reserved");
73 assert!(tag > 0, "record tag 0 is reserved");
74 Self {
75 subsystem,
76 version,
77 tag,
78 }
79 }
80
81 pub fn subsystem(&self) -> u8 {
83 self.subsystem
84 }
85
86 pub fn version(&self) -> u8 {
88 self.version
89 }
90
91 pub fn tag(&self) -> u8 {
93 self.tag
94 }
95
96 pub fn from_bytes(data: &[u8]) -> Result<Self, DeserializeError> {
106 if data.len() < 3 {
107 return Err(DeserializeError {
108 message: format!(
109 "buffer too short for key prefix: need 3 bytes, got {}",
110 data.len()
111 ),
112 });
113 }
114 let subsystem = validate_subsystem(data[0])?;
115 let version = validate_key_version(data[1])?;
116 let tag = validate_record_tag(data[2])?;
117 Ok(Self {
118 subsystem,
119 version,
120 tag,
121 })
122 }
123
124 pub fn from_bytes_with_validation(
136 data: &[u8],
137 expected_subsystem: u8,
138 expected_version: u8,
139 ) -> Result<Self, DeserializeError> {
140 let prefix = Self::from_bytes(data)?;
141 if prefix.subsystem != expected_subsystem {
142 return Err(DeserializeError {
143 message: format!(
144 "invalid subsystem: expected 0x{:02x}, got 0x{:02x}",
145 expected_subsystem, prefix.subsystem
146 ),
147 });
148 }
149 if prefix.version != expected_version {
150 return Err(DeserializeError {
151 message: format!(
152 "invalid key version: expected 0x{:02x}, got 0x{:02x}",
153 expected_version, prefix.version
154 ),
155 });
156 }
157 Ok(prefix)
158 }
159
160 pub fn to_bytes(&self) -> Bytes {
162 Bytes::from(vec![self.subsystem, self.version, self.tag])
163 }
164
165 pub fn write_to(&self, buf: &mut BytesMut) {
167 buf.put_u8(self.subsystem);
168 buf.put_u8(self.version);
169 buf.put_u8(self.tag);
170 }
171}
172
173fn validate_key_version(byte: u8) -> Result<u8, DeserializeError> {
174 if byte == 0 {
175 return Err(DeserializeError {
176 message: format!(
177 "invalid key version: 0x{:02x} (version 0 is reserved)",
178 byte
179 ),
180 });
181 }
182 Ok(byte)
183}
184
185fn validate_subsystem(byte: u8) -> Result<u8, DeserializeError> {
186 if byte == 0 {
187 return Err(DeserializeError {
188 message: format!(
189 "invalid subsystem: 0x{:02x} (subsystem 0 is reserved)",
190 byte
191 ),
192 });
193 }
194 Ok(byte)
195}
196
197fn validate_record_tag(byte: u8) -> Result<u8, DeserializeError> {
198 if byte == 0 {
199 return Err(DeserializeError {
200 message: format!("invalid record tag: 0x{:02x} (tag 0 is reserved)", byte),
201 });
202 }
203 Ok(byte)
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn should_create_key_prefix() {
212 let subsystem = 0x10;
214 let version = 0x01;
215 let tag = 0x02;
216
217 let prefix = KeyPrefix::new(subsystem, version, tag);
219
220 assert_eq!(prefix.subsystem(), subsystem);
222 assert_eq!(prefix.version(), version);
223 assert_eq!(prefix.tag(), tag);
224 }
225
226 #[test]
227 #[should_panic(expected = "subsystem 0 is reserved")]
228 fn should_panic_on_zero_subsystem() {
229 KeyPrefix::new(0, 0x01, 0x02);
230 }
231
232 #[test]
233 #[should_panic(expected = "key version 0 is reserved")]
234 fn should_panic_on_zero_version() {
235 KeyPrefix::new(0x10, 0, 0x02);
236 }
237
238 #[test]
239 #[should_panic(expected = "record tag 0 is reserved")]
240 fn should_panic_on_zero_record_tag() {
241 KeyPrefix::new(0x10, 0x01, 0);
242 }
243
244 #[test]
245 fn should_parse_tag_from_byte() {
246 let bytes = [0x42, 0x17, 0x24];
248
249 let key_prefix = KeyPrefix::from_bytes(&bytes).unwrap();
251
252 assert_eq!(key_prefix.subsystem(), 0x42);
254 assert_eq!(key_prefix.version(), 0x17);
255 assert_eq!(key_prefix.tag(), 0x24);
256 }
257
258 #[test]
259 fn should_reject_zero_subsystem_byte() {
260 let bytes = [0x00, 0x17, 0x53];
262
263 let result = KeyPrefix::from_bytes(&bytes);
265
266 assert!(
268 matches!(result, Err(DeserializeError { message }) if message.contains("subsystem 0 is reserved"))
269 );
270 }
271
272 #[test]
273 fn should_reject_zero_version_byte() {
274 let bytes = [0x53, 0x00, 0x24];
276
277 let result = KeyPrefix::from_bytes(&bytes);
279
280 assert!(
282 matches!(result, Err(DeserializeError { message }) if message.contains("version 0 is reserved"))
283 );
284 }
285
286 #[test]
287 fn should_reject_zero_record_type_byte() {
288 let bytes = [0x53, 0x17, 0x00];
290
291 let result = KeyPrefix::from_bytes(&bytes);
293
294 assert!(
296 matches!(result, Err(DeserializeError { message }) if message.contains("tag 0 is reserved"))
297 );
298 }
299
300 #[test]
301 fn should_write_and_read_key_prefix() {
302 let prefix = KeyPrefix::new(0x10, 0x01, 0x02);
304 let mut buf = BytesMut::new();
305
306 prefix.write_to(&mut buf);
308 let parsed = KeyPrefix::from_bytes(&buf).unwrap();
309
310 assert_eq!(parsed, prefix);
312 }
313
314 #[test]
315 fn should_serialize_key_prefix_to_bytes() {
316 let prefix = KeyPrefix::new(0x10, 0x01, 0x02);
318
319 let bytes = prefix.to_bytes();
321
322 assert_eq!(bytes.len(), 3);
324 assert_eq!(bytes[0], 0x10);
325 assert_eq!(bytes[1], 0x01);
326 assert_eq!(bytes[2], 0x02);
327 }
328
329 #[test]
330 fn should_parse_key_prefix_with_validation() {
331 let expected_subsystem = 0x10;
333 let expected_version = 0x01;
334 let data = [expected_subsystem, expected_version, 0x25];
335
336 let prefix =
338 KeyPrefix::from_bytes_with_validation(&data, expected_subsystem, expected_version)
339 .unwrap();
340
341 assert_eq!(prefix.subsystem(), expected_subsystem);
343 assert_eq!(prefix.version(), expected_version);
344 assert_eq!(prefix.tag(), 0x25);
345 }
346
347 #[test]
348 fn should_reject_wrong_subsystem() {
349 let data = [0x10, 0x01, 0x25];
351
352 let result = KeyPrefix::from_bytes_with_validation(&data, 0x20, 0x01);
354
355 assert!(
357 matches!(result, Err(DeserializeError { message }) if message.contains("invalid subsystem"))
358 );
359 }
360
361 #[test]
362 fn should_reject_wrong_version() {
363 let data = [0x10, 0x02, 0x10]; let result = KeyPrefix::from_bytes_with_validation(&data, 0x10, 0x01);
368
369 assert!(
371 matches!(result, Err(DeserializeError { message }) if message.contains("invalid key version"))
372 );
373 }
374
375 #[test]
376 fn should_reject_short_buffer() {
377 let data = [0x01, 0x10]; let result = KeyPrefix::from_bytes(&data);
382
383 assert!(
385 matches!(result, Err(DeserializeError { message }) if message.contains("buffer too short"))
386 );
387 }
388}