1use crate::{EXTENDED_TLV_HEADER_SIZE, EXTENDED_TYPE_MARKER, LanceError, Result, TLV_HEADER_SIZE};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[repr(u8)]
19pub enum RecordType {
20 Data = 0x00,
22 Json = 0x01,
24 MsgPack = 0x02,
26 Protobuf = 0x03,
28 Avro = 0x04,
30 Control = 0xFE,
32 Extended = 0xFF,
34}
35
36impl TryFrom<u8> for RecordType {
37 type Error = LanceError;
38
39 fn try_from(value: u8) -> Result<Self> {
40 match value {
41 0x01 => Ok(Self::Json),
42 0x02 => Ok(Self::MsgPack),
43 0x03 => Ok(Self::Protobuf),
44 0x04 => Ok(Self::Avro),
45 0xFE => Ok(Self::Control),
46 0xFF => Ok(Self::Extended),
47 _ => Ok(Self::Data),
49 }
50 }
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub struct Header {
56 pub record_type: RecordType,
58 pub extended_type: u16,
60 pub length: u32,
62 pub header_size: usize,
64}
65
66impl Header {
67 #[must_use]
69 pub const fn new(record_type: RecordType, length: u32) -> Self {
70 Self {
71 record_type,
72 extended_type: 0,
73 length,
74 header_size: TLV_HEADER_SIZE,
75 }
76 }
77
78 #[must_use]
80 pub const fn new_extended(extended_type: u16, length: u32) -> Self {
81 Self {
82 record_type: RecordType::Extended,
83 extended_type,
84 length,
85 header_size: EXTENDED_TLV_HEADER_SIZE,
86 }
87 }
88
89 #[must_use]
91 pub const fn total_size(&self) -> usize {
92 self.header_size + self.length as usize
93 }
94
95 #[must_use]
97 pub fn encode(&self) -> Vec<u8> {
98 if self.record_type == RecordType::Extended {
99 let mut buf = Vec::with_capacity(EXTENDED_TLV_HEADER_SIZE);
100 buf.push(EXTENDED_TYPE_MARKER);
101 buf.extend_from_slice(&self.extended_type.to_le_bytes());
102 buf.extend_from_slice(&self.length.to_le_bytes());
103 buf
104 } else {
105 let mut buf = Vec::with_capacity(TLV_HEADER_SIZE);
106 buf.push(self.record_type as u8);
107 buf.extend_from_slice(&self.length.to_le_bytes());
108 buf
109 }
110 }
111}
112
113pub fn parse_header(data: &[u8]) -> Result<Header> {
121 if data.len() < TLV_HEADER_SIZE {
122 return Err(LanceError::BufferTooSmall {
123 required: TLV_HEADER_SIZE,
124 available: data.len(),
125 });
126 }
127
128 let type_byte = data[0];
129
130 if type_byte == EXTENDED_TYPE_MARKER {
131 if data.len() < EXTENDED_TLV_HEADER_SIZE {
133 return Err(LanceError::BufferTooSmall {
134 required: EXTENDED_TLV_HEADER_SIZE,
135 available: data.len(),
136 });
137 }
138
139 let extended_type = u16::from_le_bytes([data[1], data[2]]);
140 let length = u32::from_le_bytes([data[3], data[4], data[5], data[6]]);
141
142 Ok(Header {
143 record_type: RecordType::Extended,
144 extended_type,
145 length,
146 header_size: EXTENDED_TLV_HEADER_SIZE,
147 })
148 } else {
149 let record_type = RecordType::try_from(type_byte)?;
151 let length = u32::from_le_bytes([data[1], data[2], data[3], data[4]]);
152
153 Ok(Header {
154 record_type,
155 extended_type: 0,
156 length,
157 header_size: TLV_HEADER_SIZE,
158 })
159 }
160}
161
162#[cfg(test)]
163#[allow(clippy::unwrap_used)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn test_standard_header_roundtrip() {
169 let header = Header::new(RecordType::Json, 1024);
170 let encoded = header.encode();
171 assert_eq!(encoded.len(), TLV_HEADER_SIZE);
172
173 let parsed = parse_header(&encoded).unwrap();
174 assert_eq!(parsed.record_type, RecordType::Json);
175 assert_eq!(parsed.length, 1024);
176 assert_eq!(parsed.header_size, TLV_HEADER_SIZE);
177 }
178
179 #[test]
180 fn test_extended_header_roundtrip() {
181 let header = Header::new_extended(0x1234, 65536);
182 let encoded = header.encode();
183 assert_eq!(encoded.len(), EXTENDED_TLV_HEADER_SIZE);
184
185 let parsed = parse_header(&encoded).unwrap();
186 assert_eq!(parsed.record_type, RecordType::Extended);
187 assert_eq!(parsed.extended_type, 0x1234);
188 assert_eq!(parsed.length, 65536);
189 assert_eq!(parsed.header_size, EXTENDED_TLV_HEADER_SIZE);
190 }
191
192 #[test]
193 fn test_buffer_too_small() {
194 let data = [0x00, 0x01, 0x02]; let result = parse_header(&data);
196 assert!(result.is_err());
197 }
198
199 #[test]
200 fn test_total_size() {
201 let header = Header::new(RecordType::Data, 100);
202 assert_eq!(header.total_size(), TLV_HEADER_SIZE + 100);
203
204 let ext_header = Header::new_extended(0x01, 200);
205 assert_eq!(ext_header.total_size(), EXTENDED_TLV_HEADER_SIZE + 200);
206 }
207}