1use crate::{Error, Result};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[non_exhaustive]
16pub enum DataType {
17 Boolean = 0x01,
19 Integer8 = 0x02,
21 Integer16 = 0x03,
23 Integer32 = 0x04,
25 Unsigned8 = 0x05,
27 Unsigned16 = 0x06,
29 Unsigned32 = 0x07,
31 Real32 = 0x08,
33 Real64 = 0x11,
35 Integer64 = 0x15,
37 Unsigned64 = 0x1B,
39}
40
41impl DataType {
42 pub const fn index(self) -> u16 {
44 self as u16
45 }
46
47 pub const fn from_index(index: u16) -> Option<Self> {
51 Some(match index {
52 0x01 => DataType::Boolean,
53 0x02 => DataType::Integer8,
54 0x03 => DataType::Integer16,
55 0x04 => DataType::Integer32,
56 0x05 => DataType::Unsigned8,
57 0x06 => DataType::Unsigned16,
58 0x07 => DataType::Unsigned32,
59 0x08 => DataType::Real32,
60 0x11 => DataType::Real64,
61 0x15 => DataType::Integer64,
62 0x1B => DataType::Unsigned64,
63 _ => return None,
64 })
65 }
66
67 pub const fn size(self) -> usize {
69 match self {
70 DataType::Boolean | DataType::Integer8 | DataType::Unsigned8 => 1,
71 DataType::Integer16 | DataType::Unsigned16 => 2,
72 DataType::Integer32 | DataType::Unsigned32 | DataType::Real32 => 4,
73 DataType::Integer64 | DataType::Unsigned64 | DataType::Real64 => 8,
74 }
75 }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq)]
80#[non_exhaustive]
81pub enum Value {
82 Boolean(bool),
84 Integer8(i8),
86 Integer16(i16),
88 Integer32(i32),
90 Unsigned8(u8),
92 Unsigned16(u16),
94 Unsigned32(u32),
96 Real32(f32),
98 Real64(f64),
100 Integer64(i64),
102 Unsigned64(u64),
104}
105
106impl Value {
107 pub const fn data_type(&self) -> DataType {
109 match self {
110 Value::Boolean(_) => DataType::Boolean,
111 Value::Integer8(_) => DataType::Integer8,
112 Value::Integer16(_) => DataType::Integer16,
113 Value::Integer32(_) => DataType::Integer32,
114 Value::Unsigned8(_) => DataType::Unsigned8,
115 Value::Unsigned16(_) => DataType::Unsigned16,
116 Value::Unsigned32(_) => DataType::Unsigned32,
117 Value::Real32(_) => DataType::Real32,
118 Value::Real64(_) => DataType::Real64,
119 Value::Integer64(_) => DataType::Integer64,
120 Value::Unsigned64(_) => DataType::Unsigned64,
121 }
122 }
123
124 pub const fn size(&self) -> usize {
126 self.data_type().size()
127 }
128
129 pub fn encode_le(&self, buf: &mut [u8]) -> Result<usize> {
134 let n = self.size();
135 if buf.len() < n {
136 return Err(Error::BadLength);
137 }
138 match self {
139 Value::Boolean(v) => buf[0] = *v as u8,
140 Value::Integer8(v) => buf[0] = *v as u8,
141 Value::Unsigned8(v) => buf[0] = *v,
142 Value::Integer16(v) => buf[..2].copy_from_slice(&v.to_le_bytes()),
143 Value::Unsigned16(v) => buf[..2].copy_from_slice(&v.to_le_bytes()),
144 Value::Integer32(v) => buf[..4].copy_from_slice(&v.to_le_bytes()),
145 Value::Unsigned32(v) => buf[..4].copy_from_slice(&v.to_le_bytes()),
146 Value::Real32(v) => buf[..4].copy_from_slice(&v.to_le_bytes()),
147 Value::Integer64(v) => buf[..8].copy_from_slice(&v.to_le_bytes()),
148 Value::Unsigned64(v) => buf[..8].copy_from_slice(&v.to_le_bytes()),
149 Value::Real64(v) => buf[..8].copy_from_slice(&v.to_le_bytes()),
150 }
151 Ok(n)
152 }
153
154 pub fn decode_le(data_type: DataType, bytes: &[u8]) -> Result<Value> {
159 if bytes.len() != data_type.size() {
160 return Err(Error::BadLength);
161 }
162 let v = match data_type {
163 DataType::Boolean => Value::Boolean(bytes[0] != 0),
164 DataType::Integer8 => Value::Integer8(bytes[0] as i8),
165 DataType::Unsigned8 => Value::Unsigned8(bytes[0]),
166 DataType::Integer16 => Value::Integer16(i16::from_le_bytes([bytes[0], bytes[1]])),
167 DataType::Unsigned16 => Value::Unsigned16(u16::from_le_bytes([bytes[0], bytes[1]])),
168 DataType::Integer32 => {
169 Value::Integer32(i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
170 }
171 DataType::Unsigned32 => {
172 Value::Unsigned32(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
173 }
174 DataType::Real32 => {
175 Value::Real32(f32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
176 }
177 DataType::Integer64 => Value::Integer64(i64::from_le_bytes([
178 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
179 ])),
180 DataType::Unsigned64 => Value::Unsigned64(u64::from_le_bytes([
181 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
182 ])),
183 DataType::Real64 => Value::Real64(f64::from_le_bytes([
184 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
185 ])),
186 };
187 Ok(v)
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn sizes_match_spec() {
197 assert_eq!(DataType::Boolean.size(), 1);
198 assert_eq!(DataType::Unsigned16.size(), 2);
199 assert_eq!(DataType::Unsigned32.size(), 4);
200 assert_eq!(DataType::Real64.size(), 8);
201 }
202
203 #[test]
204 fn data_type_index_roundtrips() {
205 for dt in [
206 DataType::Boolean,
207 DataType::Unsigned32,
208 DataType::Real64,
209 DataType::Integer64,
210 DataType::Unsigned64,
211 ] {
212 assert_eq!(DataType::from_index(dt.index()), Some(dt));
213 }
214 assert_eq!(DataType::Unsigned32.index(), 0x0007);
215 assert_eq!(DataType::from_index(0x09), None);
217 }
218
219 #[test]
220 fn u32_is_little_endian() {
221 let mut buf = [0u8; 8];
222 let n = Value::Unsigned32(0x1234_5678).encode_le(&mut buf).unwrap();
223 assert_eq!(n, 4);
224 assert_eq!(&buf[..4], &[0x78, 0x56, 0x34, 0x12]);
225 }
226
227 #[test]
228 fn decode_roundtrips() {
229 let cases = [
230 Value::Boolean(true),
231 Value::Integer8(-5),
232 Value::Unsigned16(0xBEEF),
233 Value::Integer32(-123456),
234 Value::Unsigned32(0xDEAD_BEEF),
235 Value::Unsigned64(0x0102_0304_0506_0708),
236 ];
237 let mut buf = [0u8; 8];
238 for v in cases {
239 let n = v.encode_le(&mut buf).unwrap();
240 let back = Value::decode_le(v.data_type(), &buf[..n]).unwrap();
241 assert_eq!(v, back);
242 }
243 }
244
245 #[test]
246 fn encode_rejects_short_buffer() {
247 let mut buf = [0u8; 2];
248 assert_eq!(
249 Value::Unsigned32(0).encode_le(&mut buf),
250 Err(Error::BadLength)
251 );
252 }
253
254 #[test]
255 fn decode_rejects_wrong_length() {
256 assert_eq!(
257 Value::decode_le(DataType::Unsigned32, &[0, 0]),
258 Err(Error::BadLength)
259 );
260 }
261}