1pub mod ltv;
7
8use crate::packet_encoding::{Encodable, Error as PacketError};
9use std::str::FromStr;
10
11pub type Address = [u8; 6];
15
16#[repr(u8)]
18#[derive(Clone, Copy, Debug, PartialEq)]
19pub enum AddressType {
20 Public = 0x00,
21 Random = 0x01,
22}
23
24impl AddressType {
25 pub const BYTE_SIZE: usize = 1;
26}
27
28impl TryFrom<u8> for AddressType {
29 type Error = PacketError;
30
31 fn try_from(value: u8) -> Result<Self, Self::Error> {
32 match value {
33 0x00 => Ok(Self::Public),
34 0x01 => Ok(Self::Random),
35 _ => Err(PacketError::OutOfRange),
36 }
37 }
38}
39
40impl FromStr for AddressType {
41 type Err = PacketError;
42
43 fn from_str(s: &str) -> Result<Self, Self::Err> {
44 match s {
45 "Public" => Ok(AddressType::Public),
46 "Random" => Ok(AddressType::Random),
47 _ => Err(PacketError::InvalidParameter(format!("invalid address type: {s}"))),
48 }
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
56pub struct AdvertisingSetId(u8);
57
58impl AdvertisingSetId {
59 pub const BYTE_SIZE: usize = 1;
61
62 pub const MAX_VALUE: u8 = 0x0F;
64
65 pub fn value(&self) -> u8 {
66 self.0
67 }
68}
69
70impl TryFrom<u8> for AdvertisingSetId {
71 type Error = PacketError;
72
73 fn try_from(value: u8) -> Result<Self, Self::Error> {
74 if value > Self::MAX_VALUE {
75 Err(PacketError::OutOfRange)
76 } else {
77 Ok(Self(value))
78 }
79 }
80}
81
82impl From<AdvertisingSetId> for u8 {
83 fn from(sid: AdvertisingSetId) -> u8 {
84 sid.0
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq)]
90pub struct PeriodicAdvertisingInterval(pub u16);
91
92impl PeriodicAdvertisingInterval {
93 pub const BYTE_SIZE: usize = 2;
94 pub const UNKNOWN_VALUE: u16 = 0xFFFF;
95
96 pub const fn unknown() -> Self {
97 Self(Self::UNKNOWN_VALUE)
98 }
99}
100
101impl Encodable for PeriodicAdvertisingInterval {
102 type Error = PacketError;
103
104 fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> {
106 if buf.len() < Self::BYTE_SIZE {
107 return Err(PacketError::BufferTooSmall);
108 }
109 buf[0..Self::BYTE_SIZE].copy_from_slice(&self.0.to_le_bytes());
110 Ok(())
111 }
112
113 fn encoded_len(&self) -> core::primitive::usize {
114 Self::BYTE_SIZE
115 }
116}
117
118#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
122pub enum CodingFormat {
123 MuLawLog,
124 ALawLog,
125 Cvsd,
126 Transparent,
127 LinearPcm,
128 Msbc,
129 Lc3,
130 G729a,
131 VendorSpecific,
132 Unrecognized(u8),
133}
134
135impl From<u8> for CodingFormat {
136 fn from(value: u8) -> Self {
137 match value {
138 0x00 => Self::MuLawLog,
139 0x01 => Self::ALawLog,
140 0x02 => Self::Cvsd,
141 0x03 => Self::Transparent,
142 0x04 => Self::LinearPcm,
143 0x05 => Self::Msbc,
144 0x06 => Self::Lc3,
145 0x07 => Self::G729a,
146 0xFF => Self::VendorSpecific,
147 x => Self::Unrecognized(x),
148 }
149 }
150}
151
152impl From<CodingFormat> for u8 {
153 fn from(value: CodingFormat) -> Self {
154 match value {
155 CodingFormat::MuLawLog => 0x00,
156 CodingFormat::ALawLog => 0x01,
157 CodingFormat::Cvsd => 0x02,
158 CodingFormat::Transparent => 0x03,
159 CodingFormat::LinearPcm => 0x04,
160 CodingFormat::Msbc => 0x05,
161 CodingFormat::Lc3 => 0x06,
162 CodingFormat::G729a => 0x07,
163 CodingFormat::VendorSpecific => 0xFF,
164 CodingFormat::Unrecognized(x) => x,
165 }
166 }
167}
168
169impl core::fmt::Display for CodingFormat {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 match self {
172 CodingFormat::MuLawLog => write!(f, "ยต-law log"),
173 CodingFormat::ALawLog => write!(f, "A-law log"),
174 CodingFormat::Cvsd => write!(f, "CVSD"),
175 CodingFormat::Transparent => write!(f, "Transparent"),
176 CodingFormat::LinearPcm => write!(f, "Linear PCM"),
177 CodingFormat::Msbc => write!(f, "mSBC"),
178 CodingFormat::Lc3 => write!(f, "LC3"),
179 CodingFormat::G729a => write!(f, "G.729A"),
180 CodingFormat::VendorSpecific => write!(f, "Vendor Specific"),
181 CodingFormat::Unrecognized(x) => write!(f, "Unrecognized ({x})"),
182 }
183 }
184}
185
186#[derive(Debug, Clone, PartialEq)]
188pub enum CodecId {
189 Assigned(CodingFormat),
192 VendorSpecific {
193 company_id: crate::CompanyId,
194 vendor_specific_codec_id: u16,
195 },
196}
197
198impl CodecId {
199 pub const BYTE_SIZE: usize = 5;
200}
201
202impl crate::packet_encoding::Decodable for CodecId {
203 type Error = crate::packet_encoding::Error;
204
205 fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
206 if buf.len() < 5 {
207 return (Err(crate::packet_encoding::Error::UnexpectedDataLength), buf.len());
208 }
209 let format = buf[0].into();
210 if format != CodingFormat::VendorSpecific {
211 return (Ok(Self::Assigned(format)), 5);
213 }
214 let company_id = u16::from_le_bytes([buf[1], buf[2]]).into();
215 let vendor_specific_codec_id = u16::from_le_bytes([buf[3], buf[4]]);
216 (Ok(Self::VendorSpecific { company_id, vendor_specific_codec_id }), 5)
217 }
218}
219
220impl crate::packet_encoding::Encodable for CodecId {
221 type Error = crate::packet_encoding::Error;
222
223 fn encoded_len(&self) -> core::primitive::usize {
224 Self::BYTE_SIZE
225 }
226
227 fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> {
228 if buf.len() < Self::BYTE_SIZE {
229 return Err(Self::Error::BufferTooSmall);
230 }
231 match self {
232 CodecId::Assigned(format) => {
233 buf[0] = (*format).into();
234 buf[1..5].fill(0);
235 }
236 CodecId::VendorSpecific { company_id, vendor_specific_codec_id } => {
237 buf[0] = 0xFF;
238 [buf[1], buf[2]] = u16::from(*company_id).to_le_bytes();
239 [buf[3], buf[4]] = vendor_specific_codec_id.to_le_bytes();
240 }
241 }
242 Ok(())
243 }
244}
245
246#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247pub enum Phy {
248 Le1m,
250 Le2m,
252 LeCoded,
254}
255
256#[cfg(test)]
257mod tests {
258 use crate::packet_encoding::Decodable;
259
260 use super::*;
261 use std::str::FromStr;
262
263 #[test]
264 fn address_type_from_str() {
265 let addr_type = AddressType::from_str("Public").expect("should succeed");
266 assert_eq!(addr_type, AddressType::Public);
267 let addr_type = AddressType::from_str("Random").expect("should succeed");
268 assert_eq!(addr_type, AddressType::Random);
269 AddressType::from_str("invalid").expect_err("should fail");
270 }
271
272 #[test]
273 fn encode_pa_interval() {
274 let mut buf = [0; PeriodicAdvertisingInterval::BYTE_SIZE];
275 let interval = PeriodicAdvertisingInterval(0x1004);
276
277 interval.encode(&mut buf[..]).expect("should succeed");
278 assert_eq!(buf, [0x04, 0x10]);
279 }
280
281 #[test]
282 fn encode_pa_interval_fails() {
283 let mut buf = [0; 1]; let interval = PeriodicAdvertisingInterval(0x1004);
285
286 interval.encode(&mut buf[..]).expect_err("should fail");
287 }
288
289 #[test]
290 fn decode_codec_id() {
291 let assigned = [0x01, 0x00, 0x00, 0x00, 0x00];
292 let (codec_id, _) = CodecId::decode(&assigned[..]);
293 assert_eq!(codec_id, Ok(CodecId::Assigned(CodingFormat::ALawLog)));
294
295 let vendor_specific = [0xFF, 0x36, 0xFD, 0x11, 0x22];
296 let (codec_id, _) = CodecId::decode(&vendor_specific[..]);
297 assert_eq!(
298 codec_id,
299 Ok(CodecId::VendorSpecific {
300 company_id: (0xFD36 as u16).into(),
301 vendor_specific_codec_id: 0x2211
302 })
303 );
304 }
305
306 #[test]
307 fn encode_codec_id() {
308 let assigned = [0x01, 0x00, 0x00, 0x00, 0x00];
309 let (codec_id, _) = CodecId::decode(&assigned[..]);
310 assert_eq!(codec_id, Ok(CodecId::Assigned(CodingFormat::ALawLog)));
311
312 let vendor_specific = [0xFF, 0x36, 0xFD, 0x11, 0x22];
313 let (codec_id, _) = CodecId::decode(&vendor_specific[..]);
314 assert_eq!(
315 codec_id,
316 Ok(CodecId::VendorSpecific {
317 company_id: (0xFD36 as u16).into(),
318 vendor_specific_codec_id: 0x2211
319 })
320 );
321 }
322
323 #[test]
324 fn advertising_set_id_success() {
325 assert_eq!(AdvertisingSetId::try_from(0x00).unwrap().value(), 0x00);
326 assert_eq!(AdvertisingSetId::try_from(0x0F).unwrap().value(), 0x0F);
327 }
328
329 #[test]
330 fn advertising_set_id_out_of_range() {
331 assert_eq!(AdvertisingSetId::try_from(0x10), Err(PacketError::OutOfRange));
332 assert_eq!(AdvertisingSetId::try_from(0xFF), Err(PacketError::OutOfRange));
333 }
334}