common/serde/
key_prefix.rs1use bytes::{BufMut, Bytes, BytesMut};
33
34use super::DeserializeError;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub struct KeyPrefix {
58 version: u8,
59 tag: RecordTag,
60}
61
62impl KeyPrefix {
63 pub fn new(version: u8, tag: RecordTag) -> Self {
65 Self { version, tag }
66 }
67
68 pub fn version(&self) -> u8 {
70 self.version
71 }
72
73 pub fn tag(&self) -> RecordTag {
75 self.tag
76 }
77
78 pub fn from_bytes(data: &[u8]) -> Result<Self, DeserializeError> {
86 if data.len() < 2 {
87 return Err(DeserializeError {
88 message: format!(
89 "buffer too short for key prefix: need 2 bytes, got {}",
90 data.len()
91 ),
92 });
93 }
94 let version = data[0];
95 let tag = RecordTag::from_byte(data[1])?;
96 Ok(Self { version, tag })
97 }
98
99 pub fn from_bytes_versioned(
108 data: &[u8],
109 expected_version: u8,
110 ) -> Result<Self, DeserializeError> {
111 let prefix = Self::from_bytes(data)?;
112 if prefix.version != expected_version {
113 return Err(DeserializeError {
114 message: format!(
115 "invalid key version: expected 0x{:02x}, got 0x{:02x}",
116 expected_version, prefix.version
117 ),
118 });
119 }
120 Ok(prefix)
121 }
122
123 pub fn to_bytes(&self) -> Bytes {
125 Bytes::from(vec![self.version, self.tag.as_byte()])
126 }
127
128 pub fn write_to(&self, buf: &mut BytesMut) {
130 buf.put_u8(self.version);
131 buf.put_u8(self.tag.as_byte());
132 }
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
155pub struct RecordTag(u8);
156
157impl RecordTag {
158 pub fn new(record_type: u8, reserved: u8) -> Self {
165 assert!(
166 record_type > 0 && record_type <= 0x0F,
167 "record type {} must be in range 1-15",
168 record_type
169 );
170 assert!(
171 reserved <= 0x0F,
172 "reserved bits {} must be in range 0-15",
173 reserved
174 );
175 RecordTag((record_type << 4) | reserved)
176 }
177
178 pub fn from_byte(byte: u8) -> Result<Self, DeserializeError> {
182 let record_type = (byte & 0xF0) >> 4;
183 if record_type == 0 {
184 return Err(DeserializeError {
185 message: format!(
186 "invalid record tag: 0x{:02x} (record type 0 is reserved)",
187 byte
188 ),
189 });
190 }
191 Ok(RecordTag(byte))
192 }
193
194 pub fn record_type(&self) -> u8 {
196 (self.0 & 0xF0) >> 4
197 }
198
199 pub fn reserved(&self) -> u8 {
201 self.0 & 0x0F
202 }
203
204 pub fn as_byte(&self) -> u8 {
206 self.0
207 }
208
209 pub fn with_reserved(&self, reserved: u8) -> Self {
215 assert!(
216 reserved <= 0x0F,
217 "reserved bits {} must be in range 0-15",
218 reserved
219 );
220 RecordTag((self.0 & 0xF0) | reserved)
221 }
222
223 pub fn type_range(record_type: u8) -> std::ops::Range<u8> {
228 assert!(
229 record_type > 0 && record_type <= 0x0F,
230 "record type {} must be in range 1-15",
231 record_type
232 );
233 let start = record_type << 4;
234 start..(start + 0x10)
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 #[test]
243 fn should_create_record_tag() {
244 let record_type = 0x05;
246 let reserved = 0x03;
247
248 let tag = RecordTag::new(record_type, reserved);
250
251 assert_eq!(tag.as_byte(), 0x53);
253 assert_eq!(tag.record_type(), 0x05);
254 assert_eq!(tag.reserved(), 0x03);
255 }
256
257 #[test]
258 fn should_create_tag_with_zero_reserved() {
259 let record_type = 0x01;
261 let reserved = 0x00;
262
263 let tag = RecordTag::new(record_type, reserved);
265
266 assert_eq!(tag.as_byte(), 0x10);
268 assert_eq!(tag.record_type(), 0x01);
269 assert_eq!(tag.reserved(), 0x00);
270 }
271
272 #[test]
273 fn should_create_tag_with_max_values() {
274 let record_type = 0x0F;
276 let reserved = 0x0F;
277
278 let tag = RecordTag::new(record_type, reserved);
280
281 assert_eq!(tag.as_byte(), 0xFF);
283 assert_eq!(tag.record_type(), 0x0F);
284 assert_eq!(tag.reserved(), 0x0F);
285 }
286
287 #[test]
288 #[should_panic(expected = "record type 0 must be in range 1-15")]
289 fn should_panic_on_zero_record_type() {
290 RecordTag::new(0, 0);
291 }
292
293 #[test]
294 #[should_panic(expected = "record type 16 must be in range 1-15")]
295 fn should_panic_on_record_type_overflow() {
296 RecordTag::new(16, 0);
297 }
298
299 #[test]
300 #[should_panic(expected = "reserved bits 16 must be in range 0-15")]
301 fn should_panic_on_reserved_overflow() {
302 RecordTag::new(1, 16);
303 }
304
305 #[test]
306 fn should_parse_tag_from_byte() {
307 let byte = 0x53;
309
310 let tag = RecordTag::from_byte(byte).unwrap();
312
313 assert_eq!(tag.record_type(), 0x05);
315 assert_eq!(tag.reserved(), 0x03);
316 }
317
318 #[test]
319 fn should_reject_zero_record_type_byte() {
320 let byte = 0x0F; let result = RecordTag::from_byte(byte);
325
326 assert!(result.is_err());
328 assert!(
329 result
330 .unwrap_err()
331 .message
332 .contains("record type 0 is reserved")
333 );
334 }
335
336 #[test]
337 fn should_compute_type_range() {
338 let record_type = 0x03;
340
341 let range = RecordTag::type_range(record_type);
343
344 assert_eq!(range.start, 0x30);
346 assert_eq!(range.end, 0x40);
347 }
348
349 #[test]
350 fn should_create_tag_with_different_reserved_bits() {
351 let tag = RecordTag::new(0x05, 0x00);
353
354 let new_tag = tag.with_reserved(0x0A);
356
357 assert_eq!(new_tag.record_type(), 0x05);
359 assert_eq!(new_tag.reserved(), 0x0A);
360 assert_eq!(new_tag.as_byte(), 0x5A);
361 }
362
363 #[test]
364 #[should_panic(expected = "reserved bits 16 must be in range 0-15")]
365 fn should_panic_on_with_reserved_overflow() {
366 let tag = RecordTag::new(0x01, 0x00);
367 tag.with_reserved(16);
368 }
369
370 #[test]
371 fn should_create_key_prefix() {
372 let version = 0x01;
374 let tag = RecordTag::new(0x02, 0x05);
375
376 let prefix = KeyPrefix::new(version, tag);
378
379 assert_eq!(prefix.version(), version);
381 assert_eq!(prefix.tag().as_byte(), tag.as_byte());
382 }
383
384 #[test]
385 fn should_write_and_read_key_prefix() {
386 let prefix = KeyPrefix::new(0x01, RecordTag::new(0x02, 0x05));
388 let mut buf = BytesMut::new();
389
390 prefix.write_to(&mut buf);
392 let parsed = KeyPrefix::from_bytes(&buf).unwrap();
393
394 assert_eq!(parsed, prefix);
396 }
397
398 #[test]
399 fn should_serialize_key_prefix_to_bytes() {
400 let prefix = KeyPrefix::new(0x01, RecordTag::new(0x02, 0x05));
402
403 let bytes = prefix.to_bytes();
405
406 assert_eq!(bytes.len(), 2);
408 assert_eq!(bytes[0], 0x01);
409 assert_eq!(bytes[1], 0x25);
410 }
411
412 #[test]
413 fn should_parse_key_prefix_versioned() {
414 let expected_version = 0x01;
416 let data = [expected_version, 0x25]; let prefix = KeyPrefix::from_bytes_versioned(&data, expected_version).unwrap();
420
421 assert_eq!(prefix.version(), expected_version);
423 assert_eq!(prefix.tag().record_type(), 0x02);
424 assert_eq!(prefix.tag().reserved(), 0x05);
425 }
426
427 #[test]
428 fn should_reject_wrong_version() {
429 let data = [0x02, 0x10]; let result = KeyPrefix::from_bytes_versioned(&data, 0x01);
434
435 assert!(result.is_err());
437 assert!(result.unwrap_err().message.contains("invalid key version"));
438 }
439
440 #[test]
441 fn should_reject_short_buffer() {
442 let data = [0x01]; let result = KeyPrefix::from_bytes(&data);
447
448 assert!(result.is_err());
450 assert!(result.unwrap_err().message.contains("buffer too short"));
451 }
452}