Skip to main content

rs_matter/tlv/
traits.rs

1/*
2 *
3 *    Copyright (c) 2022-2025 Project CHIP Authors
4 *
5 *    Licensed under the Apache License, Version 2.0 (the "License");
6 *    you may not use this file except in compliance with the License.
7 *    You may obtain a copy of the License at
8 *
9 *        http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *    Unless required by applicable law or agreed to in writing, software
12 *    distributed under the License is distributed on an "AS IS" BASIS,
13 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *    See the License for the specific language governing permissions and
15 *    limitations under the License.
16 */
17
18use crate::error::Error;
19use crate::utils::init;
20
21use super::{
22    EitherIter, TLVElement, TLVSequenceIter, TLVSequenceTLVIter, TLVTag, TLVValue, TLVValueType,
23    TLVWrite, TLV,
24};
25
26pub use builder::*;
27pub use container::*;
28pub use maybe::*;
29pub use octets::*;
30pub use slice::*;
31pub use str::*;
32
33mod array;
34mod bitflags;
35mod builder;
36mod container;
37mod maybe;
38mod octets;
39mod primitive;
40mod slice;
41mod str;
42mod vec;
43
44/// A trait representing Rust types that can deserialize themselves from
45/// a TLV-encoded byte slice.
46pub trait FromTLV<'a>: Sized + 'a {
47    /// Deserialize the type from a TLV-encoded element.
48    fn from_tlv(element: &TLVElement<'a>) -> Result<Self, Error>;
49
50    /// Generate an in-place initializer for the type that initializes
51    /// the type from a TLV-encoded element.
52    fn init_from_tlv(element: TLVElement<'a>) -> impl init::Init<Self, Error> {
53        unsafe {
54            init::init_from_closure(move |slot| {
55                core::ptr::write(slot, Self::from_tlv(&element)?);
56
57                Ok(())
58            })
59        }
60    }
61
62    /// Deserialize the type from a TLV-encoded element.
63    ///
64    /// Called when the deserialized value will be placed in `Nullable`
65    /// so as to check whether the value falls in the nullable range of the type, which
66    /// might be shorter than the non-nullable range of the type.
67    fn nullable_from_tlv(element: &TLVElement<'a>) -> Result<Self, Error> {
68        Self::from_tlv(element)
69    }
70
71    /// Generate an in-place initializer for the type that initializes
72    /// the type from a TLV-encoded element.
73    ///
74    /// Called when the deserialized value will be placed in `Nullable`
75    /// so as to check whether the value falls in the nullable range of the type, which
76    /// might be shorter than the non-nullable range of the type.
77    fn init_nullable_from_tlv(element: TLVElement<'a>) -> impl init::Init<Self, Error> {
78        unsafe {
79            init::init_from_closure(move |slot| {
80                core::ptr::write(slot, Self::nullable_from_tlv(&element)?);
81
82                Ok(())
83            })
84        }
85    }
86}
87
88/// A trait representing Rust types that can serialize themselves to
89/// a TLV-encoded stream.
90pub trait ToTLV {
91    /// Serialize the type to a TLV-encoded stream.
92    fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, tw: W) -> Result<(), Error>;
93
94    /// Serialize the type as an iterator of `TLV` instances by potentially borrowing
95    /// data from the type.
96    fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>>;
97
98    /// Serialize the type to a TLV-encoded stream.
99    ///
100    /// Called when the serialized value is placed in `Nullable`
101    /// so as to check whether the value falls in the nullable range of the type, which
102    /// might be shorter than the non-nullable range of the type.
103    fn nullable_to_tlv<W: TLVWrite>(&self, tag: &TLVTag, tw: W) -> Result<(), Error> {
104        self.to_tlv(tag, tw)
105    }
106
107    /// Serialize the type as an iterator of `TLV` instances by potentially borrowing
108    /// data from the type.
109    ///
110    /// Called when the serialized value is placed in `Nullable`
111    /// so as to check whether the value falls in the nullable range of the type, which
112    /// might be shorter than the non-nullable range of the type.
113    fn nullable_tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
114        self.tlv_iter(tag)
115    }
116}
117
118impl<T> ToTLV for &T
119where
120    T: ToTLV,
121{
122    fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, tw: W) -> Result<(), Error> {
123        (*self).to_tlv(tag, tw)
124    }
125
126    fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
127        (*self).tlv_iter(tag)
128    }
129
130    fn nullable_to_tlv<W: TLVWrite>(&self, tag: &TLVTag, tw: W) -> Result<(), Error> {
131        (*self).nullable_to_tlv(tag, tw)
132    }
133
134    fn nullable_tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
135        (*self).nullable_tlv_iter(tag)
136    }
137}
138
139impl<'a> FromTLV<'a> for TLVElement<'a> {
140    fn from_tlv(element: &TLVElement<'a>) -> Result<Self, Error> {
141        Ok(element.clone())
142    }
143}
144
145impl ToTLV for TLVElement<'_> {
146    fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, mut tw: W) -> Result<(), Error> {
147        if self.is_empty() {
148            // Special-case serializing empty TLV elements to nothing
149            // Useful in tests
150            Ok(())
151        } else {
152            tw.raw_value(tag, self.control()?.value_type, self.raw_value()?)
153        }
154    }
155
156    fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
157        TLVElementTLVIter::Start(tag, self.clone())
158    }
159}
160
161enum TLVElementTLVIter<'a> {
162    Start(TLVTag, TLVElement<'a>),
163    Seq(TLVSequenceTLVIter<'a>),
164    Finished,
165}
166
167impl<'a> Iterator for TLVElementTLVIter<'a> {
168    type Item = Result<TLV<'a>, Error>;
169
170    fn next(&mut self) -> Option<Self::Item> {
171        match core::mem::replace(self, Self::Finished) {
172            TLVElementTLVIter::Start(tag, elem) => {
173                if elem.is_empty() {
174                    // Special-case serializing empty TLV elements to nothing
175                    // Useful in tests
176                    None
177                } else {
178                    let value = elem.value().map(|value| TLV::new(tag, value));
179
180                    if let Ok(seq) = elem.container() {
181                        *self = Self::Seq(seq.tlv_iter());
182                    } else {
183                        *self = TLVElementTLVIter::Finished;
184                    }
185
186                    Some(value)
187                }
188            }
189            TLVElementTLVIter::Seq(mut iter) => {
190                if let Some(value) = iter.next() {
191                    *self = TLVElementTLVIter::Seq(iter);
192                    Some(value)
193                } else {
194                    Some(Ok(TLV::end_container()))
195                }
196            }
197            TLVElementTLVIter::Finished => None,
198        }
199    }
200}
201
202impl<'a> FromTLV<'a> for TLVValue<'a> {
203    fn from_tlv(element: &TLVElement<'a>) -> Result<Self, Error> {
204        element.value()
205    }
206}
207
208impl ToTLV for TLVValue<'_> {
209    fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, mut tw: W) -> Result<(), Error> {
210        tw.tlv(tag, self)
211    }
212
213    fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
214        TLV::new(tag, self.clone()).into_tlv_iter()
215    }
216}
217
218#[cfg(test)]
219mod tests {
220    use core::fmt::Debug;
221    use core::mem::MaybeUninit;
222
223    use rs_matter_macros::{FromTLV, ToTLV};
224
225    use crate::tlv::{Octets, TLVElement, TLV};
226    use crate::utils::init::InitMaybeUninit;
227    use crate::utils::storage::WriteBuf;
228
229    use super::{FromTLV, OctetStr, TLVTag, ToTLV};
230
231    fn test_from_tlv<'a, T: FromTLV<'a> + PartialEq + Debug>(data: &'a [u8], expected: T) {
232        let root = TLVElement::new(data);
233        let test = T::from_tlv(&root).unwrap();
234        ::core::assert_eq!(test, expected);
235
236        let test_init = T::init_from_tlv(root);
237
238        let mut test = MaybeUninit::<T>::uninit();
239
240        let test = test.try_init_with(test_init).unwrap();
241
242        ::core::assert_eq!(*test, expected);
243    }
244
245    fn test_to_tlv<T: ToTLV>(t: T, expected: &[u8]) {
246        let mut buf = [0; 20];
247        let mut tw = WriteBuf::new(&mut buf);
248
249        t.to_tlv(&TLVTag::Anonymous, &mut tw).unwrap();
250
251        assert_eq!(tw.as_slice(), expected);
252
253        tw.reset();
254
255        let mut iter = t
256            .tlv_iter(TLVTag::Anonymous)
257            .flat_map(TLV::result_into_bytes_iter);
258        loop {
259            match iter.next() {
260                Some(Ok(byte)) => tw.append(&[byte]).unwrap(),
261                None => break,
262                _ => panic!("Error in iterator"),
263            }
264        }
265
266        ::core::assert_eq!(tw.as_slice(), expected);
267    }
268
269    #[derive(ToTLV, Debug)]
270    struct TestDerive {
271        a: u16,
272        b: u32,
273    }
274
275    #[test]
276    fn test_derive_totlv() {
277        test_to_tlv(
278            TestDerive {
279                a: 0x1010,
280                b: 0x20202020,
281            },
282            &[21, 37, 0, 0x10, 0x10, 38, 1, 0x20, 0x20, 0x20, 0x20, 24],
283        );
284    }
285
286    #[derive(FromTLV, Debug, PartialEq)]
287    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
288    struct TestDeriveSimple {
289        a: u16,
290        b: u32,
291    }
292
293    #[test]
294    fn test_derive_fromtlv() {
295        test_from_tlv(
296            &[21, 37, 0, 10, 0, 38, 1, 20, 0, 0, 0, 24],
297            TestDeriveSimple { a: 10, b: 20 },
298        );
299    }
300
301    #[derive(FromTLV, Debug, PartialEq)]
302    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
303    #[tlvargs(lifetime = "'a")]
304    struct TestDeriveStr<'a> {
305        a: u16,
306        b: OctetStr<'a>,
307    }
308
309    #[test]
310    fn test_derive_fromtlv_str() {
311        test_from_tlv(
312            &[21, 37, 0, 10, 0, 0x30, 0x01, 0x03, 10, 11, 12, 0],
313            TestDeriveStr {
314                a: 10,
315                b: Octets(&[10, 11, 12]),
316            },
317        );
318    }
319
320    #[derive(FromTLV, Debug, PartialEq)]
321    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
322    struct TestDeriveOption {
323        a: u16,
324        b: Option<u16>,
325        c: Option<u16>,
326    }
327
328    #[test]
329    fn test_derive_fromtlv_option() {
330        test_from_tlv(
331            &[21, 37, 0, 10, 0, 37, 2, 11, 0],
332            TestDeriveOption {
333                a: 10,
334                b: None,
335                c: Some(11),
336            },
337        );
338    }
339
340    #[derive(FromTLV, ToTLV, Debug, PartialEq)]
341    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
342    struct TestDeriveFabScoped {
343        a: u16,
344        #[tagval(0xFE)]
345        fab_idx: u16,
346    }
347
348    #[test]
349    fn test_derive_fromtlv_fab_scoped() {
350        test_from_tlv(
351            &[21, 37, 0, 10, 0, 37, 0xFE, 11, 0],
352            TestDeriveFabScoped { a: 10, fab_idx: 11 },
353        );
354    }
355
356    #[test]
357    fn test_derive_totlv_fab_scoped() {
358        test_to_tlv(
359            TestDeriveFabScoped { a: 20, fab_idx: 3 },
360            &[21, 36, 0, 20, 36, 0xFE, 3, 24],
361        );
362    }
363
364    #[derive(ToTLV, FromTLV, PartialEq, Debug)]
365    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
366    enum TestDeriveEnum {
367        ValueA(u32),
368        ValueB(u32),
369    }
370
371    #[test]
372    fn test_derive_from_to_tlv_enum() {
373        // Test FromTLV
374        test_from_tlv(&[21, 36, 0, 100, 24, 0], TestDeriveEnum::ValueA(100));
375
376        // Test ToTLV
377        test_to_tlv(TestDeriveEnum::ValueB(10), &[21, 36, 1, 10, 24]);
378    }
379}