1use crate::error::{Result, Tlv8Error};
12use crate::reader::Tlv8Reader;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct Tlv8Map {
17 items: Vec<(u8, Vec<u8>)>,
18}
19
20impl Tlv8Map {
21 pub fn parse(bytes: &[u8]) -> Result<Self> {
27 Ok(Self::from_items(Tlv8Reader::parse(bytes)?))
28 }
29
30 pub fn from_items(items: Vec<(u8, Vec<u8>)>) -> Self {
32 Self { items }
33 }
34
35 pub fn items(&self) -> &[(u8, Vec<u8>)] {
37 &self.items
38 }
39
40 pub fn into_items(self) -> Vec<(u8, Vec<u8>)> {
42 self.items
43 }
44
45 pub fn get(&self, ty: u8) -> Option<&[u8]> {
47 self.items
48 .iter()
49 .find(|(t, _)| *t == ty)
50 .map(|(_, v)| v.as_slice())
51 }
52
53 fn get_uint(&self, ty: u8, width: usize) -> Result<Option<u64>> {
54 let Some(bytes) = self.get(ty) else {
55 return Ok(None);
56 };
57 if bytes.len() > width {
58 return Err(Tlv8Error::IntegerTooLarge {
59 requested: width,
60 actual: bytes.len(),
61 });
62 }
63 let mut buf = [0u8; 8];
64 buf[..bytes.len()].copy_from_slice(bytes);
65 Ok(Some(u64::from_le_bytes(buf)))
66 }
67
68 pub fn get_u8(&self, ty: u8) -> Result<Option<u8>> {
74 #[allow(clippy::cast_possible_truncation)]
76 Ok(self.get_uint(ty, 1)?.map(|v| v as u8))
77 }
78
79 pub fn get_u16(&self, ty: u8) -> Result<Option<u16>> {
85 #[allow(clippy::cast_possible_truncation)]
87 Ok(self.get_uint(ty, 2)?.map(|v| v as u16))
88 }
89
90 pub fn get_u32(&self, ty: u8) -> Result<Option<u32>> {
96 #[allow(clippy::cast_possible_truncation)]
98 Ok(self.get_uint(ty, 4)?.map(|v| v as u32))
99 }
100
101 pub fn get_u64(&self, ty: u8) -> Result<Option<u64>> {
107 self.get_uint(ty, 8)
108 }
109}
110
111#[cfg(test)]
112#[allow(clippy::unwrap_used, clippy::expect_used)]
114mod tests {
115 use super::*;
116 use crate::Tlv8Error;
117
118 #[test]
119 fn get_returns_first_matching_value() {
120 let map = Tlv8Map::parse(&[0x01, 0x02, 0xAB, 0xCD, 0x02, 0x01, 0xEE]).unwrap();
121 assert_eq!(map.get(0x01), Some(&[0xAB, 0xCD][..]));
122 assert_eq!(map.get(0x02), Some(&[0xEE][..]));
123 assert_eq!(map.get(0x09), None);
124 }
125
126 #[test]
127 fn get_u8_decodes_single_byte() {
128 let map = Tlv8Map::parse(&[0x02, 0x01, 0x2A]).unwrap();
129 assert_eq!(map.get_u8(0x02).unwrap(), Some(0x2A));
130 assert_eq!(map.get_u8(0x09).unwrap(), None);
131 }
132
133 #[test]
134 fn get_u16_decodes_le() {
135 let map = Tlv8Map::parse(&[0x03, 0x02, 0x34, 0x12]).unwrap();
136 assert_eq!(map.get_u16(0x03).unwrap(), Some(0x1234));
137 }
138
139 #[test]
140 fn get_u32_decodes_le() {
141 let map = Tlv8Map::parse(&[0x04, 0x04, 0xBE, 0xBA, 0xFE, 0xCA]).unwrap();
142 assert_eq!(map.get_u32(0x04).unwrap(), Some(0xCAFE_BABE));
143 }
144
145 #[test]
146 fn get_u64_decodes_le() {
147 let bytes = [0x05, 0x08, 0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01];
148 let map = Tlv8Map::parse(&bytes).unwrap();
149 assert_eq!(map.get_u64(0x05).unwrap(), Some(0x0123_4567_89AB_CDEF));
150 }
151
152 #[test]
153 fn get_uint_accepts_shorter_than_width() {
154 let map = Tlv8Map::parse(&[0x04, 0x01, 0x07]).unwrap();
156 assert_eq!(map.get_u32(0x04).unwrap(), Some(7));
157 }
158
159 #[test]
160 fn get_u16_rejects_oversized_value() {
161 let map = Tlv8Map::parse(&[0x03, 0x03, 0x01, 0x02, 0x03]).unwrap();
162 assert_eq!(
163 map.get_u16(0x03).unwrap_err(),
164 Tlv8Error::IntegerTooLarge {
165 requested: 2,
166 actual: 3
167 }
168 );
169 }
170
171 #[test]
172 fn from_items_and_into_items_round_trip() {
173 let items = vec![(0x01_u8, vec![0xAA]), (0x02, vec![0xBB, 0xCC])];
174 let map = Tlv8Map::from_items(items.clone());
175 assert_eq!(map.items(), &items[..]);
176 assert_eq!(map.into_items(), items);
177 }
178}