1use std::collections::BTreeMap;
2
3use crate::{
4 endian::{read_uint16, read_uint32, uint16, uint32},
5 error::{EmapiError, Result},
6};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct TlvEntry {
10 pub tag: u8,
11 pub value: Vec<u8>,
12}
13
14impl TlvEntry {
15 pub fn new(tag: u8, value: impl Into<Vec<u8>>) -> Self {
16 Self {
17 tag,
18 value: value.into(),
19 }
20 }
21
22 pub fn try_new(tag: u16, value: impl Into<Vec<u8>>) -> Result<Self> {
23 if tag > 0xFF {
24 return Err(EmapiError::Range {
25 name: "tag",
26 min: 0,
27 max: 0xFF,
28 value: u64::from(tag),
29 });
30 }
31 Ok(Self::new(tag as u8, value))
32 }
33
34 pub fn uint8(tag: u8, value: u8) -> Result<Self> {
35 Ok(Self::new(tag, [value]))
36 }
37
38 pub fn uint16(tag: u8, value: u16) -> Result<Self> {
39 Ok(Self::new(tag, uint16(value)))
40 }
41
42 pub fn uint32(tag: u8, value: u32) -> Result<Self> {
43 Ok(Self::new(tag, uint32(value)))
44 }
45
46 pub fn string(tag: u8, value: &str) -> Result<Self> {
47 let mut bytes = value.as_bytes().to_vec();
48 bytes.push(0x00);
49 Ok(Self::new(tag, bytes))
50 }
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct TlvData {
55 entries: BTreeMap<u8, TlvEntry>,
56}
57
58impl TlvData {
59 pub fn get(&self, tag: u8) -> Option<&TlvEntry> {
60 self.entries.get(&tag)
61 }
62
63 pub fn contains_tag(&self, tag: u8) -> bool {
64 self.entries.contains_key(&tag)
65 }
66
67 pub fn uint8(&self, tag: u8) -> Result<Option<u8>> {
68 let Some(value) = self.value(tag) else {
69 return Ok(None);
70 };
71 if value.len() != 1 {
72 return Err(EmapiError::InvalidTlvType {
73 tag,
74 expected: "uint8",
75 actual_len: value.len(),
76 });
77 }
78 Ok(Some(value[0]))
79 }
80
81 pub fn uint16(&self, tag: u8) -> Result<Option<u16>> {
82 let Some(value) = self.value(tag) else {
83 return Ok(None);
84 };
85 if value.len() != 2 {
86 return Err(EmapiError::InvalidTlvType {
87 tag,
88 expected: "uint16",
89 actual_len: value.len(),
90 });
91 }
92 Ok(Some(read_uint16(value, 0)?))
93 }
94
95 pub fn uint32(&self, tag: u8) -> Result<Option<u32>> {
96 let Some(value) = self.value(tag) else {
97 return Ok(None);
98 };
99 if value.len() != 4 {
100 return Err(EmapiError::InvalidTlvType {
101 tag,
102 expected: "uint32",
103 actual_len: value.len(),
104 });
105 }
106 Ok(Some(read_uint32(value, 0)?))
107 }
108
109 pub fn string(&self, tag: u8) -> Result<Option<String>> {
110 let Some(value) = self.value(tag) else {
111 return Ok(None);
112 };
113 let end = if value.last() == Some(&0x00) {
114 value.len() - 1
115 } else {
116 value.len()
117 };
118 String::from_utf8(value[..end].to_vec())
119 .map(Some)
120 .map_err(|_| EmapiError::InvalidUtf8)
121 }
122
123 fn value(&self, tag: u8) -> Option<&[u8]> {
124 self.entries.get(&tag).map(|entry| entry.value.as_slice())
125 }
126}
127
128pub struct Tlv;
129
130impl Tlv {
131 pub fn encode(entries: &[TlvEntry]) -> Result<Vec<u8>> {
132 let mut result = Vec::new();
133 for entry in entries {
134 if entry.value.len() > usize::from(u16::MAX) {
135 return Err(EmapiError::Range {
136 name: "length",
137 min: 0,
138 max: u64::from(u16::MAX),
139 value: entry.value.len() as u64,
140 });
141 }
142 result.push(entry.tag);
143 result.extend_from_slice(&uint16(entry.value.len() as u16));
144 result.extend_from_slice(&entry.value);
145 }
146 Ok(result)
147 }
148
149 pub fn decode(data: &[u8]) -> Result<TlvData> {
150 let mut entries = BTreeMap::new();
151 let mut offset = 0;
152 while offset < data.len() {
153 if offset + 3 > data.len() {
154 return Err(EmapiError::TruncatedTlvHeader);
155 }
156 let tag = data[offset];
157 let length = usize::from(read_uint16(data, offset + 1)?);
158 let value_start = offset + 3;
159 let value_end = value_start + length;
160 if value_end > data.len() {
161 return Err(EmapiError::TruncatedTlvValue);
162 }
163 entries.insert(
164 tag,
165 TlvEntry::new(tag, data[value_start..value_end].to_vec()),
166 );
167 offset = value_end;
168 }
169 Ok(TlvData { entries })
170 }
171}