Skip to main content

rs_matter/tlv/traits/
skippable.rs

1/*
2 *
3 *    Copyright (c) 2026 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 pinned_init::init_from_closure;
19
20use crate::error::Error;
21use crate::tlv::{FromTLV, TLVElement, TLVTag, TLVWrite, ToTLV, TLV};
22use crate::utils::init::{try_init, Init, InitDefault, IntoFallibleInit};
23
24/// A wrapper for a type `T` which might not always be present in the TLV stream.
25///
26/// Unlike `Option<T>` and `Optional<T>` though, `Skippable<T>` is NOT initialized
27/// to `None` when the value is not present in the TLV stream, but to a default `T`.
28#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
30pub struct Skippable<T> {
31    value: T,
32}
33
34impl<T> Skippable<T> {
35    /// Create a new Skippable with the given value.
36    pub const fn new(value: T) -> Self {
37        Self { value }
38    }
39
40    /// Initialize a Skippable with the given initializer.
41    pub fn init<I: Init<T, E>, E>(value: I) -> impl Init<Self, E> {
42        try_init!(Self {
43            value <- value,
44        }? E)
45    }
46
47    pub const fn value(&self) -> &T {
48        &self.value
49    }
50
51    pub const fn value_mut(&mut self) -> &mut T {
52        &mut self.value
53    }
54}
55
56impl<T> Skippable<T>
57where
58    T: Default + InitDefault,
59{
60    /// Create a new Skippable with the default value of T.
61    pub fn new_default() -> Self {
62        Self::new(T::default())
63    }
64
65    /// Initialize a Skippable with the default value of T.
66    pub fn init_default() -> impl Init<Self> {
67        Self::init(T::init_default().into_fallible())
68    }
69}
70
71impl<T> Default for Skippable<T>
72where
73    T: Default,
74{
75    fn default() -> Self {
76        Self::new(T::default())
77    }
78}
79
80impl<T> InitDefault for Skippable<T>
81where
82    T: InitDefault,
83{
84    fn init_default() -> impl Init<Self> {
85        // Inline the inherent initializer's body rather than call
86        // `Self::init_default()`, which would resolve back to this trait
87        // method and recurse.
88        Self::init(T::init_default().into_fallible())
89    }
90}
91
92impl<T> core::ops::Deref for Skippable<T> {
93    type Target = T;
94
95    fn deref(&self) -> &Self::Target {
96        &self.value
97    }
98}
99
100impl<T> core::ops::DerefMut for Skippable<T> {
101    fn deref_mut(&mut self) -> &mut Self::Target {
102        &mut self.value
103    }
104}
105
106impl<'a, T> FromTLV<'a> for Skippable<T>
107where
108    T: FromTLV<'a> + Default + InitDefault,
109{
110    fn from_tlv(element: &TLVElement<'a>) -> Result<Self, Error> {
111        if element.is_empty() {
112            Ok(Self::new_default())
113        } else {
114            Ok(Self::new(T::from_tlv(element)?))
115        }
116    }
117
118    fn init_from_tlv(element: TLVElement<'a>) -> impl Init<Self, Error> {
119        unsafe {
120            init_from_closure(move |slot| {
121                if element.is_empty() {
122                    Self::init(T::init_default().into_fallible()).__init(slot)
123                } else {
124                    Self::init(T::init_from_tlv(element)).__init(slot)
125                }
126            })
127        }
128    }
129}
130
131impl<T> ToTLV for Skippable<T>
132where
133    T: ToTLV,
134{
135    fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, tw: W) -> Result<(), Error> {
136        self.value.to_tlv(tag, tw)
137    }
138
139    fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
140        self.value.tlv_iter(tag)
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use crate::tlv::{FromTLV, TLVElement, TLVTag, ToTLV};
147    use crate::utils::init::InitMaybeUninit;
148    use crate::utils::storage::{Vec, WriteBuf};
149
150    use super::Skippable;
151
152    /// Serialize `t` (anonymous tag) into a fresh buffer and return the bytes.
153    fn to_bytes<T: ToTLV>(t: &T, buf: &mut [u8]) -> usize {
154        let mut wb = WriteBuf::new(buf);
155        t.to_tlv(&TLVTag::Anonymous, &mut wb).unwrap();
156        wb.get_tail()
157    }
158
159    // `Skippable<T>` requires `T: Default + InitDefault`, so these tests use
160    // `Vec<u16, N>` (which implements both) rather than a bare primitive.
161    type Inner = Vec<u16, 4>;
162
163    fn inner(items: &[u16]) -> Inner {
164        let mut v = Inner::new();
165        for &i in items {
166            v.push(i).unwrap();
167        }
168        v
169    }
170
171    /// A present value round-trips through `to_tlv` / `from_tlv`.
172    #[test]
173    fn present_value_roundtrips() {
174        let mut buf = [0u8; 32];
175        let len = to_bytes(&Skippable::new(inner(&[42, 43])), &mut buf);
176
177        let back = Skippable::<Inner>::from_tlv(&TLVElement::new(&buf[..len])).unwrap();
178        assert_eq!(back.value().as_slice(), &[42, 43]);
179    }
180
181    /// `from_tlv` on an EMPTY element yields the default value (not an error, and
182    /// not a `None` — `Skippable` has no `None` variant).
183    #[test]
184    fn missing_value_from_tlv_is_default() {
185        let empty = TLVElement::new(&[]);
186        assert!(empty.is_empty());
187
188        let s = Skippable::<Inner>::from_tlv(&empty).unwrap();
189        assert!(s.value().is_empty());
190    }
191
192    /// `init_from_tlv` mirrors `from_tlv`: default on empty, parsed otherwise.
193    #[test]
194    fn init_from_tlv_defaults_on_empty_and_parses_otherwise() {
195        // Empty -> default.
196        let mut slot = core::mem::MaybeUninit::<Skippable<Inner>>::uninit();
197        let s = slot
198            .try_init_with(Skippable::<Inner>::init_from_tlv(TLVElement::new(&[])))
199            .unwrap();
200        assert!(s.value().is_empty());
201
202        // Present -> parsed.
203        let mut buf = [0u8; 32];
204        let len = to_bytes(&Skippable::new(inner(&[7])), &mut buf);
205        let mut slot = core::mem::MaybeUninit::<Skippable<Inner>>::uninit();
206        let s = slot
207            .try_init_with(Skippable::<Inner>::init_from_tlv(TLVElement::new(
208                &buf[..len],
209            )))
210            .unwrap();
211        assert_eq!(s.value().as_slice(), &[7]);
212    }
213
214    /// The real use case (mirrors `Fabric` gaining a trailing `groups` field):
215    /// an older struct lacking the trailing `Skippable` field deserializes into
216    /// the newer struct with that field defaulted.
217    #[test]
218    fn missing_trailing_skippable_field_deserializes_to_default() {
219        // Older shape: two fields, no trailing field.
220        #[derive(Debug, ToTLV)]
221        struct Old {
222            a: u16,
223            b: u16,
224        }
225
226        // Newer shape: same positional tags plus a trailing `Skippable` field.
227        #[derive(Debug, FromTLV)]
228        struct New {
229            a: u16,
230            b: u16,
231            trailing: Skippable<Vec<u16, 4>>,
232        }
233
234        let mut buf = [0u8; 64];
235        let len = to_bytes(&Old { a: 7, b: 9 }, &mut buf);
236
237        let new = New::from_tlv(&TLVElement::new(&buf[..len])).unwrap();
238        assert_eq!((new.a, new.b), (7, 9));
239        assert!(new.trailing.value().is_empty());
240    }
241
242    /// A written `Skippable` field is read back unchanged (round-trip within a
243    /// struct, both fields present).
244    #[test]
245    fn trailing_skippable_field_roundtrips_when_present() {
246        #[derive(Debug, FromTLV, ToTLV)]
247        struct S {
248            a: u16,
249            trailing: Skippable<Vec<u16, 4>>,
250        }
251
252        let mut inner = Vec::new();
253        inner.push(1).unwrap();
254        inner.push(2).unwrap();
255
256        let mut buf = [0u8; 64];
257        let len = to_bytes(
258            &S {
259                a: 5,
260                trailing: Skippable::new(inner),
261            },
262            &mut buf,
263        );
264
265        let back = S::from_tlv(&TLVElement::new(&buf[..len])).unwrap();
266        assert_eq!(back.a, 5);
267        assert_eq!(back.trailing.value().as_slice(), &[1, 2]);
268    }
269}