trouble_host/types/
gatt_traits.rs

1use core::{mem, slice};
2
3use bt_hci::uuid::BluetoothUuid16;
4use heapless::{String, Vec};
5
6#[derive(Debug, PartialEq, Eq, Clone, Copy)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8/// Error type to signify an issue when converting from GATT bytes to a concrete type
9pub enum FromGattError {
10    /// Byte array's length did not match what was expected for the converted type
11    InvalidLength,
12    /// Attempt to encode as string failed due to an invalid character representation in the byte array
13    InvalidCharacter,
14}
15
16/// Trait to allow conversion of a fixed size type to and from a byte slice
17#[allow(private_bounds)]
18pub trait FixedGattValue: FromGatt {
19    /// Size of the type in bytes
20    const SIZE: usize;
21
22    /// Converts from gatt bytes.
23    /// Must return FromGattError::InvalidLength if data.len != Self::SIZE
24    fn from_gatt(data: &[u8]) -> Result<Self, FromGattError>;
25
26    /// Converts to gatt bytes.
27    /// Must return a slice of len Self::SIZE
28    fn as_gatt(&self) -> &[u8];
29}
30
31/// Trait to allow conversion of a type to gatt bytes
32pub trait AsGatt: Sized {
33    /// The minimum size the type might be
34    const MIN_SIZE: usize;
35    /// The maximum size the type might be
36    const MAX_SIZE: usize;
37    /// Converts to gatt bytes.
38    /// Must return a slice of len in MIN_SIZE..=MAX_SIZE
39    fn as_gatt(&self) -> &[u8];
40}
41
42/// Trait to allow conversion of gatt bytes into a type
43///
44/// Requires that the type implements AsGatt
45pub trait FromGatt: AsGatt {
46    /// Converts from gatt bytes.
47    /// Must return FromGattError::InvalidLength if data.len not in MIN_SIZE..=MAX_SIZE
48    fn from_gatt(data: &[u8]) -> Result<Self, FromGattError>;
49}
50
51impl<T: FixedGattValue> FromGatt for T {
52    fn from_gatt(data: &[u8]) -> Result<Self, FromGattError> {
53        <Self as FixedGattValue>::from_gatt(data)
54    }
55}
56
57impl<T: FixedGattValue> AsGatt for T {
58    const MIN_SIZE: usize = Self::SIZE;
59    const MAX_SIZE: usize = Self::SIZE;
60
61    fn as_gatt(&self) -> &[u8] {
62        <Self as FixedGattValue>::as_gatt(self)
63    }
64}
65
66trait Primitive: Copy {}
67impl Primitive for u8 {}
68impl Primitive for u16 {}
69impl Primitive for u32 {}
70impl Primitive for u64 {}
71impl Primitive for i8 {}
72impl Primitive for i16 {}
73impl Primitive for i32 {}
74impl Primitive for i64 {}
75impl Primitive for f32 {}
76impl Primitive for f64 {}
77impl Primitive for BluetoothUuid16 {} // ok as this is just a NewType(u16)
78
79impl<T: Primitive> FixedGattValue for T {
80    const SIZE: usize = mem::size_of::<Self>();
81
82    fn from_gatt(data: &[u8]) -> Result<Self, FromGattError> {
83        if data.len() != Self::SIZE {
84            Err(FromGattError::InvalidLength)
85        } else {
86            // SAFETY
87            // - Pointer is considered "valid" as per the rules outlined for validity in std::ptr v1.82.0
88            // - Pointer was generated from a slice of bytes matching the size of the type implementing Primitive, and all types implementing Primitive are valid for all possible configurations of bits
89            // - Primitive trait is constrained to require Copy
90            unsafe { Ok((data.as_ptr() as *const Self).read_unaligned()) }
91        }
92    }
93
94    fn as_gatt(&self) -> &[u8] {
95        // SAFETY
96        // - Slice is of type u8 so data is guaranteed valid for reads of any length
97        // - Data and len are tied to the address and size of the type
98        unsafe { slice::from_raw_parts(self as *const Self as *const u8, Self::SIZE) }
99    }
100}
101
102impl FixedGattValue for bool {
103    const SIZE: usize = 1;
104
105    fn from_gatt(data: &[u8]) -> Result<Self, FromGattError> {
106        if data.len() != Self::SIZE {
107            Err(FromGattError::InvalidLength)
108        } else {
109            Ok(data != [0x00])
110        }
111    }
112
113    fn as_gatt(&self) -> &[u8] {
114        match self {
115            true => &[0x01],
116            false => &[0x00],
117        }
118    }
119}
120
121impl<const N: usize> FromGatt for Vec<u8, N> {
122    fn from_gatt(data: &[u8]) -> Result<Self, FromGattError> {
123        Self::from_slice(data).map_err(|_| FromGattError::InvalidLength)
124    }
125}
126
127impl<const N: usize> AsGatt for Vec<u8, N> {
128    const MIN_SIZE: usize = 0;
129    const MAX_SIZE: usize = N;
130
131    fn as_gatt(&self) -> &[u8] {
132        self
133    }
134}
135
136impl<const N: usize> FromGatt for [u8; N] {
137    fn from_gatt(data: &[u8]) -> Result<Self, FromGattError> {
138        if data.len() <= Self::MAX_SIZE {
139            let mut actual = [0; N];
140            actual[..data.len()].copy_from_slice(data);
141            Ok(actual)
142        } else {
143            data.try_into().map_err(|_| FromGattError::InvalidLength)
144        }
145    }
146}
147
148impl<const N: usize> AsGatt for [u8; N] {
149    const MIN_SIZE: usize = 0;
150    const MAX_SIZE: usize = N;
151
152    fn as_gatt(&self) -> &[u8] {
153        self.as_slice()
154    }
155}
156
157impl<const N: usize> FromGatt for String<N> {
158    fn from_gatt(data: &[u8]) -> Result<Self, FromGattError> {
159        String::from_utf8(unwrap!(Vec::from_slice(data).map_err(|_| FromGattError::InvalidLength)))
160            .map_err(|_| FromGattError::InvalidCharacter)
161    }
162}
163
164impl<const N: usize> AsGatt for String<N> {
165    const MIN_SIZE: usize = 0;
166    const MAX_SIZE: usize = N;
167
168    fn as_gatt(&self) -> &[u8] {
169        self.as_ref()
170    }
171}
172
173impl AsGatt for &'static str {
174    const MIN_SIZE: usize = 0;
175    const MAX_SIZE: usize = usize::MAX;
176
177    fn as_gatt(&self) -> &[u8] {
178        self.as_bytes()
179    }
180}
181
182impl AsGatt for &'static [u8] {
183    const MIN_SIZE: usize = 0;
184    const MAX_SIZE: usize = usize::MAX;
185
186    fn as_gatt(&self) -> &[u8] {
187        self
188    }
189}
190
191impl AsGatt for crate::types::uuid::Uuid {
192    const MIN_SIZE: usize = 2;
193    const MAX_SIZE: usize = 16;
194
195    fn as_gatt(&self) -> &[u8] {
196        self.as_raw()
197    }
198}
199
200impl FromGatt for crate::types::uuid::Uuid {
201    fn from_gatt(data: &[u8]) -> Result<Self, FromGattError> {
202        Self::try_from(data).map_err(|_| FromGattError::InvalidLength)
203    }
204}