rs_matter/tlv/traits/container.rs
1/*
2 *
3 * Copyright (c) 2024-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
18//! A container type (`TLVContainer`) and an iterator type (`TLVContainerIter`) that represent and iterate directly over serialized TLV containers.
19//! As such, the memory prepresentation of `TLVContainer` and `TLVContainerIter` is just a byte slice (`&[u8]`),
20//! and the container elements are materialized (with `FromTLV`) only when the container is iterated over.
21//!
22//! The difference between `TLVContainer` and `TLVContainerIter` on one side, and `TLVElement`, `TLVSequence` and `TLVSequenceIter` on the other
23//! is that the former are generified by type `T: FromTLV<'_>` and can directly yield values of type `T` when iterated over,
24//! while iterating over a `TLVSequence` with a `TLVSequenceIter` always yields elements of type `TLVElement`.
25//!
26//! Thus, a `TLVContainer<TLVElement<'_>, ()`> is equivalent to a `TLVElement` which represents a container and
27//! `TLVContainerIter<TLVElement<'_>>` is equivalent to a `TLVSequenceIter<'_>` that is obtained by `element.container()?.iter()`.
28
29use core::fmt;
30use core::marker::PhantomData;
31
32use crate::error::Error;
33use crate::utils::init;
34
35use super::{EitherIter, FromTLV, TLVElement, TLVSequenceIter, TLVTag, TLVWrite, ToTLV, TLV};
36
37/// A type-state that indicates that the container can be any type of container (array, list or struct).
38pub type AnyContainer = ();
39
40/// A type-state that indicates that the container should be an array.
41#[derive(Debug, Clone, PartialEq, Eq, Hash)]
42#[cfg_attr(feature = "defmt", derive(defmt::Format))]
43pub struct ArrayContainer;
44
45/// A type-state that indicates that the container should be a list.
46#[derive(Debug, Clone, PartialEq, Eq, Hash)]
47#[cfg_attr(feature = "defmt", derive(defmt::Format))]
48pub struct ListContainer;
49
50/// A type-state that indicates that the container should be a struct.
51#[derive(Debug, Clone, PartialEq, Eq, Hash)]
52#[cfg_attr(feature = "defmt", derive(defmt::Format))]
53pub struct StructContainer;
54
55/// A type alias for an array TLV container.
56pub type TLVArray<'a, T> = TLVContainer<'a, T, ArrayContainer>;
57/// A type alias for a list TLV container.
58pub type TLVList<'a, T> = TLVContainer<'a, T, ListContainer>;
59/// A type alias for a struct TLV container.
60pub type TLVStruct<'a, T> = TLVContainer<'a, T, StructContainer>;
61
62/// `TLVContainer` is an efficient (memory-wise) way to represent a serialized TLV container, in that
63/// it does not materialize the container elements until the container is iterated over.
64///
65/// Therefore, `TLVContainer` is just a wrapper (newtype) of the serialized TLV container `&[u8]` slice.
66#[derive(Clone, PartialEq, Eq, Hash)]
67#[repr(transparent)]
68pub struct TLVContainer<'a, T, C = AnyContainer> {
69 element: TLVElement<'a>,
70 _type: PhantomData<fn() -> T>,
71 _container_type: PhantomData<C>,
72}
73
74impl<'a, T, C> TLVContainer<'a, T, C>
75where
76 T: FromTLV<'a>,
77{
78 /// Creates a new `TLVContainer` from a TLV element.
79 /// The constructor does not check whether the passed slice is a valid TLV container.
80 pub const fn new_unchecked(element: TLVElement<'a>) -> Self {
81 Self {
82 element,
83 _type: PhantomData,
84 _container_type: PhantomData,
85 }
86 }
87
88 pub fn element(&self) -> &TLVElement<'a> {
89 &self.element
90 }
91
92 /// Returns an iterator over the elements of the container.
93 pub fn iter(&self) -> TLVContainerIter<'a, T> {
94 TLVContainerIter::new(unwrap!(self.element.container()).iter())
95 }
96}
97
98impl<'a, T> TLVContainer<'a, T, AnyContainer>
99where
100 T: FromTLV<'a>,
101{
102 /// Creates a new `TLVContainer` from a TLV element that can be any container.
103 pub fn new(element: TLVElement<'a>) -> Result<Self, Error> {
104 if !element.is_empty() {
105 element.container()?;
106 }
107
108 Ok(Self::new_unchecked(element))
109 }
110}
111
112impl<'a, T> TLVContainer<'a, T, ArrayContainer>
113where
114 T: FromTLV<'a>,
115{
116 /// Creates a new `TLVContainer` from a TLV element that is expected to be of type array.
117 pub fn new(element: TLVElement<'a>) -> Result<Self, Error> {
118 if !element.is_empty() {
119 element.array()?;
120 }
121
122 Ok(Self::new_unchecked(element))
123 }
124}
125
126impl<'a, T> TLVContainer<'a, T, ListContainer>
127where
128 T: FromTLV<'a>,
129{
130 /// Creates a new `TLVContainer` from a TLV element that is expected to be of type list.
131 pub fn new(element: TLVElement<'a>) -> Result<Self, Error> {
132 if !element.is_empty() {
133 element.list()?;
134 }
135
136 Ok(Self::new_unchecked(element))
137 }
138}
139
140impl<'a, T> TLVContainer<'a, T, StructContainer>
141where
142 T: FromTLV<'a>,
143{
144 /// Creates a new `TLVContainer` from a TLV element that is expected to be of type struct.
145 pub fn new(element: TLVElement<'a>) -> Result<Self, Error> {
146 if !element.is_empty() {
147 element.structure()?;
148 }
149
150 Ok(Self::new_unchecked(element))
151 }
152}
153
154impl<'a, T, C> IntoIterator for TLVContainer<'a, T, C>
155where
156 T: FromTLV<'a>,
157{
158 type Item = Result<T, Error>;
159 type IntoIter = TLVContainerIter<'a, T>;
160
161 fn into_iter(self) -> Self::IntoIter {
162 self.iter()
163 }
164}
165
166impl<'a, T, C> IntoIterator for &TLVContainer<'a, T, C>
167where
168 T: FromTLV<'a>,
169{
170 type Item = Result<T, Error>;
171 type IntoIter = TLVContainerIter<'a, T>;
172
173 fn into_iter(self) -> Self::IntoIter {
174 self.iter()
175 }
176}
177
178impl<'a, T, C> fmt::Debug for TLVContainer<'a, T, C>
179where
180 T: FromTLV<'a> + fmt::Debug,
181{
182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 write!(f, "[")?;
184
185 let mut first = true;
186
187 for elem in self.iter() {
188 if first {
189 first = false;
190 write!(f, "{elem:?}")?;
191 } else {
192 write!(f, ", {elem:?}")?;
193 }
194 }
195
196 write!(f, "]")
197 }
198}
199
200#[cfg(feature = "defmt")]
201impl<'a, T, C> defmt::Format for TLVContainer<'a, T, C>
202where
203 T: FromTLV<'a> + defmt::Format,
204{
205 fn format(&self, f: defmt::Formatter<'_>) {
206 defmt::write!(f, "[");
207
208 let mut first = true;
209
210 for elem in self.iter() {
211 if first {
212 first = false;
213 defmt::write!(f, "{:?}", elem);
214 } else {
215 defmt::write!(f, ", {:?}", elem);
216 }
217 }
218
219 defmt::write!(f, "]")
220 }
221}
222
223impl<'a, T, C> FromTLV<'a> for TLVContainer<'a, T, C>
224where
225 T: FromTLV<'a>,
226 C: 'a,
227{
228 fn from_tlv(element: &TLVElement<'a>) -> Result<Self, Error> {
229 Ok(Self::new_unchecked(element.clone()))
230 }
231}
232
233impl<T, C> ToTLV for TLVContainer<'_, T, C> {
234 fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, tw: W) -> Result<(), Error> {
235 self.element.to_tlv(tag, tw)
236 }
237
238 fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
239 self.element.tlv_iter(tag)
240 }
241}
242
243/// An iterator over a serialized TLV container.
244#[repr(transparent)]
245pub struct TLVContainerIter<'a, T> {
246 iter: TLVSequenceIter<'a>,
247 _type: PhantomData<fn() -> T>,
248}
249
250impl<'a, T> TLVContainerIter<'a, T>
251where
252 T: FromTLV<'a>,
253{
254 /// Create a new `TLVContainerIter` from a TLV sequence iterator.
255 pub const fn new(iter: TLVSequenceIter<'a>) -> Self {
256 Self {
257 iter,
258 _type: PhantomData,
259 }
260 }
261
262 pub fn try_next(&mut self) -> Option<Result<T, Error>> {
263 let tlv = self.iter.next()?;
264
265 Some(tlv.and_then(|tlv| T::from_tlv(&tlv)))
266 }
267
268 pub fn try_next_init(&mut self) -> Option<Result<impl init::Init<T, Error> + 'a, Error>> {
269 let tlv = self.iter.next()?;
270
271 Some(tlv.map(|tlv| T::init_from_tlv(tlv)))
272 }
273}
274
275impl<'a, T> Iterator for TLVContainerIter<'a, T>
276where
277 T: FromTLV<'a>,
278{
279 type Item = Result<T, Error>;
280
281 fn next(&mut self) -> Option<Self::Item> {
282 self.try_next()
283 }
284}
285
286/// A container type that can represent either a serialized TLV array or a slice of elements.
287///
288/// Necessary for the few cases in the code where deserialized TLV structures are mutated -
289/// post deserialization - with custom array data.
290#[derive(Debug, Clone)]
291#[cfg_attr(feature = "defmt", derive(defmt::Format))]
292pub enum TLVArrayOrSlice<'a, T>
293where
294 T: FromTLV<'a>,
295{
296 Array(TLVArray<'a, T>),
297 Slice(&'a [T]),
298}
299
300impl<'a, T> TLVArrayOrSlice<'a, T>
301where
302 T: FromTLV<'a>,
303{
304 /// Creates a new `TLVArrayOrSlice` from a TLV slice.
305 pub const fn new_array(array: TLVArray<'a, T>) -> Self {
306 Self::Array(array)
307 }
308
309 /// Creates a new `TLVArrayOrSlice` from a slice.
310 pub const fn new_slice(slice: &'a [T]) -> Self {
311 Self::Slice(slice)
312 }
313
314 /// Returns an iterator over the elements of the array.
315 pub fn iter(&self) -> Result<TLVArrayOrSliceIter<'a, T>, Error> {
316 match self {
317 Self::Array(array) => Ok(TLVArrayOrSliceIter::Array(array.iter())),
318 Self::Slice(slice) => Ok(TLVArrayOrSliceIter::Slice(slice.iter())),
319 }
320 }
321}
322
323impl<'a, T> FromTLV<'a> for TLVArrayOrSlice<'a, T>
324where
325 T: FromTLV<'a>,
326{
327 fn from_tlv(element: &TLVElement<'a>) -> Result<Self, Error> {
328 Ok(Self::new_array(TLVArray::new(element.clone())?))
329 }
330}
331
332impl<'a, T> ToTLV for TLVArrayOrSlice<'a, T>
333where
334 T: FromTLV<'a>,
335 T: ToTLV,
336{
337 fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, tw: W) -> Result<(), Error> {
338 match self {
339 Self::Array(array) => array.to_tlv(tag, tw),
340 Self::Slice(slice) => slice.to_tlv(tag, tw),
341 }
342 }
343
344 fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
345 match self {
346 Self::Array(array) => EitherIter::First(array.tlv_iter(tag)),
347 Self::Slice(slice) => EitherIter::Second(slice.tlv_iter(tag)),
348 }
349 }
350}
351
352/// An iterator over the `TLVArrayOrSlice` elements.
353pub enum TLVArrayOrSliceIter<'a, T> {
354 Array(TLVContainerIter<'a, T>),
355 Slice(core::slice::Iter<'a, T>),
356}
357
358impl<'a, T> Iterator for TLVArrayOrSliceIter<'a, T>
359where
360 T: FromTLV<'a> + Clone,
361{
362 type Item = Result<T, Error>;
363
364 fn next(&mut self) -> Option<Self::Item> {
365 match self {
366 Self::Array(array) => array.next(),
367 Self::Slice(slice) => slice.next().cloned().map(|t| Ok(t)),
368 }
369 }
370}
371
372// impl<'a, T: ToTLV + FromTLV<'a> + Clone> TLVArray<'a, T> {
373// pub fn get_index(&self, index: usize) -> T {
374// for (curr, element) in self.iter().enumerate() {
375// if curr == index {
376// return element;
377// }
378// }
379// panic!("Out of bounds");
380// }
381// }
382
383// // impl<'a, 'b, T> PartialEq<TLVArray<'b, T>> for TLVArray<'a, T>
384// // where
385// // T: ToTLV + FromTLV<'a> + Clone + PartialEq,
386// // 'b: 'a,
387// // {
388// // fn eq(&self, other: &TLVArray<'b, T>) -> bool {
389// // let mut iter1 = self.iter();
390// // let mut iter2 = other.iter();
391// // loop {
392// // match (iter1.next(), iter2.next()) {
393// // (None, None) => return true,
394// // (Some(x), Some(y)) => {
395// // if x != y {
396// // return false;
397// // }
398// // }
399// // _ => return false,
400// // }
401// // }
402// // }
403// // }
404
405// // impl<'a, T> PartialEq<&[T]> for TLVArray<'a, T>
406// // where
407// // T: ToTLV + FromTLV<'a> + Clone + PartialEq,
408// // {
409// // fn eq(&self, other: &&[T]) -> bool {
410// // let mut iter1 = self.iter();
411// // let mut iter2 = other.iter();
412// // loop {
413// // match (iter1.next(), iter2.next()) {
414// // (None, None) => return true,
415// // (Some(x), Some(y)) => {
416// // if x != *y {
417// // return false;
418// // }
419// // }
420// // _ => return false,
421// // }
422// // }
423// // }
424// // }
425
426// impl<'a, T> FromTLV<'a> for TLVArray<'a, T> {
427// fn from_tlv(t: TLVElement<'a>) -> Result<Self, Error> {
428// TLVArray::new(t)
429// }
430// }
431
432// impl<'a, T> ToTLV for TLVArray<'a, T> {
433// fn to_tlv(&self, tw: &mut TLVWriter, tag_type: TagType) -> Result<(), Error> {
434// tw.start_array(tag_type)?;
435// for a in self.iter() {
436// a.to_tlv(tw, TagType::Anonymous)?;
437// }
438// tw.end_container()
439// // match *self {
440// // Self::Slice(s) => {
441// // tw.start_array(tag_type)?;
442// // for a in s {
443// // a.to_tlv(tw, TagType::Anonymous)?;
444// // }
445// // tw.end_container()
446// // }
447// // Self::Ptr(t) => t.to_tlv(tw, tag_type), <-- TODO: this fails the unit tests of Cert from/to TLV
448// // }
449// }
450
451// fn tlv_iter(&self, tag: TagType) -> impl Iterator<Item = u8> + '_ {
452// empty()
453// .start_array(tag)
454// .chain(self.iter().flat_map(move |i| i.into_tlv_iter(TagType::Anonymous)))
455// .end_container()
456// }
457
458// fn into_tlv_iter(self, tag: TagType) -> impl Iterator<Item = u8> where Self: Sized {
459// empty()
460// .start_array(tag)
461// .chain(self.into_iter().flat_map(move |i| i.into_tlv_iter(TagType::Anonymous)))
462// .end_container()
463// }
464// }
465
466// impl<'a, T: Debug + ToTLV + FromTLV<'a> + Clone> Debug for TLVArray<'a, T> { // TODO: defmt
467// fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
468// write!(f, "TLVArray [")?;
469// let mut first = true;
470// for i in self.iter() {
471// if !first {
472// write!(f, ", ")?;
473// }
474
475// write!(f, "{:?}", i)?;
476// first = false;
477// }
478// write!(f, "]")
479// }
480// }