Skip to main content

rs_matter/tlv/traits/
builder.rs

1/*
2 *
3 *    Copyright (c) 2025-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
18//! This module contains the base traits for the TLV builder framework, as well
19//! as implementations of those traits for several built-in TLV types, like arrays,
20//! octet strings, Utf8 strings, nullable and optional types.
21//!
22//! Note that putting aside those base builders, all other builders are expected to be
23//! auto-generated by using the `idl_import` macro, rather than being typed by hand,
24//! which is possible but very vberbose.
25//!
26//! The following is just an example of a builder for a struct TLV type `Foo` that has three fields:
27//! ```ignore
28//! pub struct FooBuilder<P, const F: usize> {
29//!     p: P,
30//! }
31//!
32//! impl<P> FooBuilder<P, 0>
33//! where
34//!     P: TLVBuilderParent,
35//! {
36//!     pub fn new(mut p: P, tag: &TLVTag) -> Result<Self, Error> {
37//!         p.writer().start_struct(tag)?;
38//!
39//!         Ok(Self { p })
40//!     }
41//!
42//!     pub fn field1(mut self, value: i32) -> Result<FooBuilder<P, 1>, Error> {
43//!         self.p.writer().i32(&TLVTag::Context(0), value)?;
44//!
45//!         Ok(FooBuilder {
46//!             p: self.p,
47//!         })
48//!     }
49//! }
50//!
51//! impl<P> FooBuilder<P, 1>
52//! where
53//!     P: TLVBuilderParent,
54//! {
55//!     pub fn field2(mut self, value: u8) -> Result<FooBuilder<P, 2>, Error> {
56//!         value.to_tlv(&TLVTag::Context(1), self.p.writer())?;
57//!
58//!         Ok(FooBuilder {
59//!             p: self.p,
60//!         })
61//!     }
62//! }
63//!
64//! impl<P> FooBuilder<P, 2>
65//! where
66//!     P: TLVBuilderParent,
67//! {
68//!     pub fn field3(self) -> Result<BarBuilder<FooBuilder<P, 3>, 0>, Error> {
69//!         BarBuilder::new(FooBuilder { p: self.p }, &TLVTag::Context(2))
70//!     }
71//! }
72//!
73//! impl<P> FooBuilder<P, 3>
74//! where
75//!     P: TLVBuilderParent,
76//! {
77//!     pub fn finish(mut self) -> Result<P, Error> {
78//!         self.p.writer().end_container()?;
79//!
80//!         Ok(self.p)
81//!     }
82//! }
83//!
84//! impl<P, const F: usize> TLVBuilderParent for FooBuilder<P, F>
85//! where
86//!     P: TLVBuilderParent,
87//! {
88//!     type Write = P::Write;
89//!
90//!     fn writer(&mut self) -> &mut Self::Write {
91//!         self.p.writer()
92//!     }
93//!
94//!     fn into_writer(self) -> Self::Write {
95//!         self.p.into_writer()
96//!     }
97//! }
98//!
99//! impl<P> TLVBuilder<P> for FooBuilder<P, 0>
100//! where
101//!     P: TLVBuilderParent,
102//! {
103//!     fn new(parent: P, tag: &TLVTag) -> Result<Self, Error> {
104//!         Self::new(parent, tag)
105//!     }
106//! }
107//!
108//! pub struct FooArrayBuilder<P> {
109//!     p: P,
110//! }
111//!
112//! impl<P> FooArrayBuilder<P>
113//! where
114//!     P: TLVBuilderParent,
115//! {
116//!     pub fn new(mut p: P, tag: &TLVTag) -> Result<Self, Error> {
117//!         p.writer().start_array(&tag)?;
118//!
119//!         Ok(Self { p })
120//!     }
121//!
122//!     pub fn push(self) -> Result<FooBuilder<Self, 0>, Error> {
123//!         FooBuilder::new(self, &TLVTag::Anonymous)
124//!     }
125//!
126//!     pub fn end(mut self) -> Result<P, Error> {
127//!         self.p.writer().end_container()?;
128//!
129//!         Ok(self.p)
130//!     }
131//! }
132//!
133//! impl<P> TLVBuilderParent for FooArrayBuilder<P>
134//! where
135//!     P: TLVBuilderParent,
136//! {
137//!     type Write = P::Write;
138//!
139//!     fn writer(&mut self) -> &mut Self::Write {
140//!         self.p.writer()
141//!     }
142//!
143//!     fn into_writer(self) -> Self::Write {
144//!         self.p.into_writer()
145//!     }
146//! }
147//!
148//! impl<P> TLVBuilder<P> for FooArrayBuilder<P>
149//! where
150//!     P: TLVBuilderParent,
151//! {
152//!     fn new(parent: P, tag: &TLVTag) -> Result<Self, Error> {
153//!         Self::new(parent, tag)
154//!     }
155//! }
156//!
157//! pub struct BarBuilder<P, const F: usize> {
158//!     p: P,
159//! }
160//!
161//! impl<P> BarBuilder<P, 0>
162//! where
163//!     P: TLVBuilderParent,
164//! {
165//!     pub fn new(mut p: P, tag: &TLVTag) -> Result<Self, Error> {
166//!         p.writer().start_struct(tag)?;
167//!
168//!         Ok(Self { p })
169//!     }
170//!
171//!     pub fn field1(mut self, value: i32) -> Result<BarBuilder<P, 1>, Error> {
172//!         self.p.writer().i32(&TLVTag::Context(0), value)?;
173//!
174//!         Ok(BarBuilder {
175//!             p: self.p,
176//!         })
177//!     }
178//! }
179//!
180//! impl<P> BarBuilder<P, 1>
181//! where
182//!     P: TLVBuilderParent,
183//! {
184//!     pub fn finish(mut self) -> Result<P, Error> {
185//!         self.p.writer().end_container()?;
186//!
187//!         Ok(self.p)
188//!     }
189//! }
190//!
191//! impl<P, const F: usize> TLVBuilderParent for BarBuilder<P, F>
192//! where
193//!     P: TLVBuilderParent,
194//! {
195//!     type Write = P::Write;
196//!
197//!     fn writer(&mut self) -> &mut Self::Write {
198//!         self.p.writer()
199//!     }
200//!
201//!     fn into_writer(self) -> Self::Write {
202//!         self.p.into_writer()
203//!     }
204//! }
205//!
206//! impl<P> TLVBuilder<P> for BarBuilder<P, 0>
207//! where
208//!     P: TLVBuilderParent,
209//! {
210//!     fn new(parent: P, tag: &TLVTag) -> Result<Self, Error> {
211//!         Self::new(parent, tag)
212//!     }
213//! }
214//!```
215
216use core::marker::PhantomData;
217
218use crate::error::Error;
219use crate::tlv::{TLVTag, TLVWrite};
220
221use super::{Nullable, Octets, ToTLV, Utf8Str};
222
223/// The `TLVBuilder` trait is used to implement a TLV builder for a certain TLV type.
224///
225/// A TLV builder is a helper struct that allows for an ergonomic writing of its
226/// corresponding TLV type into a `TLVWrite` trait implementation.
227///
228/// The TLV builders' implementation is zero-cost, in that it is completely erased to
229/// raw TLV writes on the wrapped `TLVWrite` trait type by the compiler.
230pub trait TLVBuilder<P>: Sized
231where
232    P: TLVBuilderParent,
233{
234    /// Create a new TLV builder for the given parent and tag.
235    fn new(parent: P, tag: &TLVTag) -> Result<Self, Error>;
236
237    /// Call a closure with the builder and return the result.
238    fn with<F>(self, f: F) -> Result<P, Error>
239    where
240        F: FnOnce(Self) -> Result<P, Error>,
241    {
242        f(self)
243    }
244
245    /// Convert itself into a writer.
246    ///
247    /// Should be used when the user prefers to use the raw `TLVWrite`
248    /// interface to write the TLV type.
249    ///
250    /// NOTE:
251    /// Use this method with caution, as it will consume the builder!
252    /// You are then on your own so as to write - using the parent's writer - the correct TLV data
253    /// that is otherwise performed by the builder.
254    fn unchecked_into_parent(self) -> P;
255}
256
257/// Each `TLVBuilder<P>` trait implementation has a parent - `P`
258/// which should implement the `TLVBuilderParent` trait.
259///
260/// The `TLVBuilderParent` trait is used to provide a way for the
261/// `TLVBuilder` to access the wrapped `TLVWrite` trait, as well as to model
262/// the nesting of the TLV builders, for complex TLV types like structs-of-structs,
263/// or arrays-of-structs and so on.
264///
265/// Also - by convention - once a `TLVBuilder` implementation has finished (ended),
266/// it should return its parent type `P`. This provides for extra ertgonomics when
267/// using the builder, as the user cannot "forget" to build the complete TLV type which
268/// is expected.
269#[cfg(not(feature = "defmt"))]
270pub trait TLVBuilderParent: Sized + core::fmt::Debug {
271    /// The type of the writer.
272    type Write: TLVWrite;
273
274    /// Return a mutable reference to the writer.
275    fn writer(&mut self) -> &mut Self::Write;
276}
277
278#[cfg(feature = "defmt")]
279pub trait TLVBuilderParent: Sized + core::fmt::Debug + defmt::Format {
280    /// The type of the writer.
281    type Write: TLVWrite;
282
283    /// Return a mutable reference to the writer.
284    fn writer(&mut self) -> &mut Self::Write;
285}
286
287/// A root-level TLV builder parent, that wraps a `TLVWrite` implementation.
288pub struct TLVWriteParent<S, W>(S, W);
289
290impl<S, W> TLVWriteParent<S, W> {
291    /// Create a new `TLVWriteParent` for the given writer.
292    pub const fn new(id: S, writer: W) -> Self {
293        Self(id, writer)
294    }
295}
296
297impl<S, W> core::fmt::Debug for TLVWriteParent<S, W>
298where
299    S: core::fmt::Debug,
300{
301    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
302        write!(f, "{:?}", self.0)
303    }
304}
305
306#[cfg(feature = "defmt")]
307impl<S, W> defmt::Format for TLVWriteParent<S, W>
308where
309    S: core::fmt::Debug + defmt::Format,
310{
311    fn format(&self, fmt: defmt::Formatter) {
312        defmt::write!(fmt, "{:?}", self.0);
313    }
314}
315
316#[cfg(not(feature = "defmt"))]
317impl<S, W> TLVBuilderParent for TLVWriteParent<S, W>
318where
319    S: core::fmt::Debug,
320    W: TLVWrite,
321{
322    type Write = W;
323
324    fn writer(&mut self) -> &mut Self::Write {
325        &mut self.1
326    }
327}
328
329#[cfg(feature = "defmt")]
330impl<S, W> TLVBuilderParent for TLVWriteParent<S, W>
331where
332    S: core::fmt::Debug + defmt::Format,
333    W: TLVWrite,
334{
335    type Write = W;
336
337    fn writer(&mut self) -> &mut Self::Write {
338        &mut self.1
339    }
340}
341
342/// The `ToTLVBuilder` is a helper struct that allows for an ergonomic writing of
343/// a TLV type that implements the `ToTLV` trait into a `TLVWrite` implementation.
344///
345/// This implementation is useful when the `T` type implements `ToTLV`.
346///
347/// Note that this implementation should be avoided when `T` has a large size, as it
348/// needs the `T` instance to be materialized prior to writing into the TLV.
349pub struct ToTLVBuilder<P, T> {
350    parent: P,
351    tag: TLVTag,
352    _t: PhantomData<fn() -> T>,
353}
354
355impl<P, T> ToTLVBuilder<P, T>
356where
357    P: TLVBuilderParent,
358    T: ToTLV + 'static,
359{
360    /// Create a new `ToTLVBuilder` for the given parent.
361    pub fn new(parent: P, tag: &TLVTag) -> Self {
362        Self {
363            parent,
364            tag: tag.clone(),
365            _t: PhantomData,
366        }
367    }
368
369    /// Write the TLV type into the writer.
370    #[cfg(not(feature = "defmt"))]
371    pub fn set(mut self, tlv: &T) -> Result<P, Error>
372    where
373        T: core::fmt::Debug,
374    {
375        #[cfg(feature = "log")]
376        log::debug!("{:?}::TLV -> {:?} +", self, tlv);
377
378        tlv.to_tlv(&self.tag, self.parent.writer())?;
379
380        Ok(self.parent)
381    }
382
383    #[cfg(feature = "defmt")]
384    pub fn set(mut self, tlv: &T) -> Result<P, Error>
385    where
386        T: core::fmt::Debug + defmt::Format,
387    {
388        defmt::debug!("{:?}::TLV[] -> {:?} +", self, tlv);
389
390        tlv.to_tlv(&self.tag, self.parent.writer())?;
391
392        Ok(self.parent)
393    }
394}
395
396impl<P, T> core::fmt::Debug for ToTLVBuilder<P, T>
397where
398    P: core::fmt::Debug,
399{
400    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
401        write!(f, "{:?}", self.parent)
402    }
403}
404
405#[cfg(feature = "defmt")]
406impl<P, T> defmt::Format for ToTLVBuilder<P, T>
407where
408    P: defmt::Format,
409{
410    fn format(&self, f: defmt::Formatter<'_>) {
411        defmt::write!(f, "{:?}", self.parent)
412    }
413}
414
415impl<P, T> TLVBuilderParent for ToTLVBuilder<P, T>
416where
417    P: TLVBuilderParent,
418{
419    type Write = P::Write;
420
421    fn writer(&mut self) -> &mut Self::Write {
422        self.parent.writer()
423    }
424}
425
426impl<P, T> TLVBuilder<P> for ToTLVBuilder<P, T>
427where
428    P: TLVBuilderParent,
429    T: ToTLV + 'static,
430{
431    fn new(parent: P, tag: &TLVTag) -> Result<Self, Error> {
432        Ok(Self::new(parent, tag))
433    }
434
435    fn unchecked_into_parent(self) -> P {
436        self.parent
437    }
438}
439
440/// The `ToTLVArrayBuilder` is a helper struct that allows for an ergonomic writing of
441/// a TLV array into a `TLVWrite` implementation.
442///
443/// This implementation is useful when the `T` elements of the array implement `ToTLV`.
444///
445/// Note that this implementation should be avoided when `T` has a large size, as it
446/// needs the `T` instances to be materialized prior to writing into the array.
447pub struct ToTLVArrayBuilder<P, T> {
448    _t: PhantomData<fn() -> T>,
449    parent: P,
450}
451
452impl<P, T> ToTLVArrayBuilder<P, T>
453where
454    P: TLVBuilderParent,
455    T: ToTLV + 'static,
456{
457    /// Create a new TLV array builder for the given parent and tag.
458    pub fn new(mut p: P, tag: &TLVTag) -> Result<Self, Error> {
459        p.writer().start_array(tag)?;
460
461        Ok(Self {
462            parent: p,
463            _t: PhantomData,
464        })
465    }
466
467    /// Push a new element into the array.
468    #[cfg(not(feature = "defmt"))]
469    pub fn push(mut self, tlv: &T) -> Result<Self, Error>
470    where
471        T: core::fmt::Debug,
472    {
473        #[cfg(feature = "log")]
474        log::debug!("{:?}::TLV[] -> {:?} +", self, tlv);
475
476        tlv.to_tlv(&TLVTag::Anonymous, self.parent.writer())?;
477
478        Ok(self)
479    }
480
481    #[cfg(feature = "defmt")]
482    pub fn push(mut self, tlv: &T) -> Result<Self, Error>
483    where
484        T: core::fmt::Debug + defmt::Format,
485    {
486        defmt::debug!("{:?}::TLV[] -> {:?} +", self, tlv);
487
488        tlv.to_tlv(&TLVTag::Anonymous, self.parent.writer())?;
489
490        Ok(self)
491    }
492
493    /// Finish the array and return the parent.
494    pub fn end(mut self) -> Result<P, Error> {
495        self.parent.writer().end_container()?;
496
497        Ok(self.parent)
498    }
499}
500
501impl<P, T> core::fmt::Debug for ToTLVArrayBuilder<P, T>
502where
503    P: core::fmt::Debug,
504{
505    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
506        write!(f, "{:?}[]", self.parent)
507    }
508}
509
510#[cfg(feature = "defmt")]
511impl<P, T> defmt::Format for ToTLVArrayBuilder<P, T>
512where
513    P: defmt::Format,
514{
515    fn format(&self, f: defmt::Formatter<'_>) {
516        defmt::write!(f, "{:?}[]", self.parent)
517    }
518}
519
520impl<P, T> TLVBuilderParent for ToTLVArrayBuilder<P, T>
521where
522    P: TLVBuilderParent,
523{
524    type Write = P::Write;
525
526    fn writer(&mut self) -> &mut Self::Write {
527        self.parent.writer()
528    }
529}
530
531impl<P, T> TLVBuilder<P> for ToTLVArrayBuilder<P, T>
532where
533    P: TLVBuilderParent,
534    T: ToTLV + 'static,
535{
536    fn new(parent: P, tag: &TLVTag) -> Result<Self, Error> {
537        Self::new(parent, tag)
538    }
539
540    fn unchecked_into_parent(self) -> P {
541        self.parent
542    }
543}
544
545/// A TLV builder for returning a Utf8 string value.
546pub struct Utf8StrBuilder<P> {
547    parent: P,
548    tag: TLVTag,
549}
550
551impl<P> Utf8StrBuilder<P>
552where
553    P: TLVBuilderParent,
554{
555    /// Create a new `Utf8StrBuilder` for the given parent.
556    pub fn new(parent: P, tag: &TLVTag) -> Self {
557        Self {
558            parent,
559            tag: tag.clone(),
560        }
561    }
562
563    /// Write the Utf8 string type into the writer.
564    pub fn set(mut self, tlv: Utf8Str<'_>) -> Result<P, Error> {
565        #[cfg(feature = "defmt")]
566        defmt::debug!("{:?}::Utf8 -> {:?} +", self, tlv);
567        #[cfg(feature = "log")]
568        ::log::debug!("{:?}::Utf8 -> {:?} +", self, tlv);
569
570        tlv.to_tlv(&self.tag, self.parent.writer())?;
571
572        Ok(self.parent)
573    }
574}
575
576impl<P> core::fmt::Debug for Utf8StrBuilder<P>
577where
578    P: core::fmt::Debug,
579{
580    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
581        write!(f, "{:?}", self.parent)
582    }
583}
584
585#[cfg(feature = "defmt")]
586impl<P> defmt::Format for Utf8StrBuilder<P>
587where
588    P: defmt::Format,
589{
590    fn format(&self, f: defmt::Formatter<'_>) {
591        defmt::write!(f, "{:?}", self.parent)
592    }
593}
594
595impl<P> TLVBuilderParent for Utf8StrBuilder<P>
596where
597    P: TLVBuilderParent,
598{
599    type Write = P::Write;
600
601    fn writer(&mut self) -> &mut Self::Write {
602        self.parent.writer()
603    }
604}
605
606impl<P> TLVBuilder<P> for Utf8StrBuilder<P>
607where
608    P: TLVBuilderParent,
609{
610    fn new(parent: P, tag: &TLVTag) -> Result<Self, Error> {
611        Ok(Self::new(parent, tag))
612    }
613
614    fn unchecked_into_parent(self) -> P {
615        self.parent
616    }
617}
618
619/// The `Utf8StrArrayBuilder` is a helper struct that allows for an ergonomic writing of
620/// a TLV array containing Utf8 strings into a `TLVWrite` implementation.
621pub struct Utf8StrArrayBuilder<P> {
622    parent: P,
623}
624
625impl<P> Utf8StrArrayBuilder<P>
626where
627    P: TLVBuilderParent,
628{
629    /// Create a new TLV Utf8 array builder for the given parent and tag.
630    pub fn new(mut p: P, tag: &TLVTag) -> Result<Self, Error> {
631        p.writer().start_array(tag)?;
632
633        Ok(Self { parent: p })
634    }
635
636    /// Push a new Utf8 string into the array.
637    pub fn push(mut self, tlv: Utf8Str<'_>) -> Result<Self, Error> {
638        #[cfg(feature = "defmt")]
639        defmt::debug!("{:?}::Utf8[] -> {:?} +", self, tlv);
640        #[cfg(feature = "log")]
641        ::log::debug!("{:?}::Utf8[] -> {:?} +", self, tlv);
642
643        tlv.to_tlv(&TLVTag::Anonymous, self.parent.writer())?;
644
645        Ok(self)
646    }
647
648    /// Finish the array and return the parent.
649    pub fn end(mut self) -> Result<P, Error> {
650        self.parent.writer().end_container()?;
651
652        Ok(self.parent)
653    }
654}
655
656impl<P> core::fmt::Debug for Utf8StrArrayBuilder<P>
657where
658    P: core::fmt::Debug,
659{
660    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
661        write!(f, "{:?}[]", self.parent)
662    }
663}
664
665#[cfg(feature = "defmt")]
666impl<P> defmt::Format for Utf8StrArrayBuilder<P>
667where
668    P: defmt::Format,
669{
670    fn format(&self, f: defmt::Formatter<'_>) {
671        defmt::write!(f, "{:?}[]", self.parent)
672    }
673}
674
675impl<P> TLVBuilderParent for Utf8StrArrayBuilder<P>
676where
677    P: TLVBuilderParent,
678{
679    type Write = P::Write;
680
681    fn writer(&mut self) -> &mut Self::Write {
682        self.parent.writer()
683    }
684}
685
686impl<P> TLVBuilder<P> for Utf8StrArrayBuilder<P>
687where
688    P: TLVBuilderParent,
689{
690    fn new(parent: P, tag: &TLVTag) -> Result<Self, Error> {
691        Self::new(parent, tag)
692    }
693
694    fn unchecked_into_parent(self) -> P {
695        self.parent
696    }
697}
698
699/// A TLV builder for returning an octet string value.
700pub struct OctetsBuilder<P> {
701    parent: P,
702    tag: TLVTag,
703}
704
705impl<P> OctetsBuilder<P>
706where
707    P: TLVBuilderParent,
708{
709    /// Create a new `OctetsBuilder` for the given parent.
710    pub fn new(parent: P, tag: &TLVTag) -> Self {
711        Self {
712            parent,
713            tag: tag.clone(),
714        }
715    }
716
717    /// Write the TLV type into the writer.
718    pub fn set(mut self, tlv: Octets<'_>) -> Result<P, Error> {
719        #[cfg(feature = "defmt")]
720        defmt::debug!("{:?}::Octets -> {:?} +", self, tlv);
721        #[cfg(feature = "log")]
722        ::log::debug!("{:?}::Octets -> {:?} +", self, tlv);
723
724        tlv.to_tlv(&self.tag, self.parent.writer())?;
725
726        Ok(self.parent)
727    }
728}
729
730impl<P> core::fmt::Debug for OctetsBuilder<P>
731where
732    P: core::fmt::Debug,
733{
734    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
735        write!(f, "{:?}", self.parent)
736    }
737}
738
739#[cfg(feature = "defmt")]
740impl<P> defmt::Format for OctetsBuilder<P>
741where
742    P: defmt::Format,
743{
744    fn format(&self, f: defmt::Formatter<'_>) {
745        defmt::write!(f, "{:?}", self.parent)
746    }
747}
748
749impl<P> TLVBuilderParent for OctetsBuilder<P>
750where
751    P: TLVBuilderParent,
752{
753    type Write = P::Write;
754
755    fn writer(&mut self) -> &mut Self::Write {
756        self.parent.writer()
757    }
758}
759
760impl<P> TLVBuilder<P> for OctetsBuilder<P>
761where
762    P: TLVBuilderParent,
763{
764    fn new(parent: P, tag: &TLVTag) -> Result<Self, Error> {
765        Ok(Self::new(parent, tag))
766    }
767
768    fn unchecked_into_parent(self) -> P {
769        self.parent
770    }
771}
772
773/// The `OctetsArrayBuilder` is a helper struct that allows for an ergonomic writing of
774/// a TLV array of octet strings into a `TLVWrite` implementation.
775pub struct OctetsArrayBuilder<P> {
776    parent: P,
777}
778
779impl<P> OctetsArrayBuilder<P>
780where
781    P: TLVBuilderParent,
782{
783    /// Create a new octets TLV array builder for the given parent and tag.
784    pub fn new(mut p: P, tag: &TLVTag) -> Result<Self, Error> {
785        p.writer().start_array(tag)?;
786
787        Ok(Self { parent: p })
788    }
789
790    /// Push a new octet string into the array.
791    pub fn push(mut self, tlv: Octets<'_>) -> Result<Self, Error> {
792        #[cfg(feature = "defmt")]
793        defmt::debug!("{:?}::Octets[] -> {:?} +", self, tlv);
794        #[cfg(feature = "log")]
795        ::log::debug!("{:?}::Octets[] -> {:?} +", self, tlv);
796
797        tlv.to_tlv(&TLVTag::Anonymous, self.parent.writer())?;
798
799        Ok(self)
800    }
801
802    /// Finish the array and return the parent.
803    pub fn end(mut self) -> Result<P, Error> {
804        self.parent.writer().end_container()?;
805
806        Ok(self.parent)
807    }
808}
809
810impl<P> core::fmt::Debug for OctetsArrayBuilder<P>
811where
812    P: core::fmt::Debug,
813{
814    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
815        write!(f, "{:?}[]", self.parent)
816    }
817}
818
819#[cfg(feature = "defmt")]
820impl<P> defmt::Format for OctetsArrayBuilder<P>
821where
822    P: defmt::Format,
823{
824    fn format(&self, f: defmt::Formatter<'_>) {
825        defmt::write!(f, "{:?}[]", self.parent)
826    }
827}
828
829impl<P> TLVBuilderParent for OctetsArrayBuilder<P>
830where
831    P: TLVBuilderParent,
832{
833    type Write = P::Write;
834
835    fn writer(&mut self) -> &mut Self::Write {
836        self.parent.writer()
837    }
838}
839
840impl<P> TLVBuilder<P> for OctetsArrayBuilder<P>
841where
842    P: TLVBuilderParent,
843{
844    fn new(parent: P, tag: &TLVTag) -> Result<Self, Error> {
845        Self::new(parent, tag)
846    }
847
848    fn unchecked_into_parent(self) -> P {
849        self.parent
850    }
851}
852
853/// A builder for a nullable TLV type.
854pub struct NullableBuilder<P, T> {
855    parent: P,
856    tag: TLVTag,
857    _t: PhantomData<fn() -> T>,
858}
859
860impl<P, T> NullableBuilder<P, T>
861where
862    P: TLVBuilderParent,
863    T: TLVBuilder<P>,
864{
865    /// Create a new nullable TLV builder for the given parent and tag.
866    pub fn new(parent: P, tag: &TLVTag) -> Self {
867        Self {
868            parent,
869            tag: tag.clone(),
870            _t: PhantomData,
871        }
872    }
873
874    /// Write a null value into the TLV and return the parent.
875    pub fn null(mut self) -> Result<P, Error> {
876        #[cfg(feature = "defmt")]
877        defmt::debug!("{:?}::nullable -> null +", self);
878        #[cfg(feature = "log")]
879        ::log::debug!("{:?}::nullable -> null +", self);
880
881        self.parent.writer().null(&self.tag)?;
882
883        Ok(self.parent)
884    }
885
886    /// Create and return the builder for the non-null value.
887    pub fn non_null(self) -> Result<T, Error> {
888        #[cfg(feature = "defmt")]
889        defmt::debug!("{:?}::nullable -> (not_null) +", self);
890        #[cfg(feature = "log")]
891        ::log::debug!("{:?}::nullable -> (not_null) +", self);
892
893        T::new(self.parent, &self.tag)
894    }
895
896    /// If `condition` is `true`, call the closure with the non-null builder.
897    /// If `condition` is `false`, write null and return the parent.
898    pub fn with_non_null_if<F>(self, condition: bool, f: F) -> Result<P, Error>
899    where
900        F: FnOnce(T) -> Result<P, Error>,
901    {
902        if condition {
903            f(self.non_null()?)
904        } else {
905            self.null()
906        }
907    }
908
909    /// If the input is non-null, call the closure with the non-null builder and the input.
910    /// If the input is null, write null and return the parent.
911    pub fn with_non_null<I, F>(self, input: Nullable<I>, f: F) -> Result<P, Error>
912    where
913        F: FnOnce(&I, T) -> Result<P, Error>,
914    {
915        if let Some(input) = input.as_opt_ref() {
916            f(input, self.non_null()?)
917        } else {
918            self.null()
919        }
920    }
921}
922
923impl<P, T> core::fmt::Debug for NullableBuilder<P, T>
924where
925    P: core::fmt::Debug,
926{
927    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
928        write!(f, "{:?}", self.parent)
929    }
930}
931
932#[cfg(feature = "defmt")]
933impl<P, T> defmt::Format for NullableBuilder<P, T>
934where
935    P: defmt::Format,
936{
937    fn format(&self, f: defmt::Formatter<'_>) {
938        defmt::write!(f, "{:?}", self.parent)
939    }
940}
941
942impl<P, T> TLVBuilderParent for NullableBuilder<P, T>
943where
944    P: TLVBuilderParent,
945{
946    type Write = P::Write;
947
948    fn writer(&mut self) -> &mut Self::Write {
949        self.parent.writer()
950    }
951}
952
953impl<P, T> TLVBuilder<P> for NullableBuilder<P, T>
954where
955    P: TLVBuilderParent,
956    T: TLVBuilder<P>,
957{
958    fn new(parent: P, tag: &TLVTag) -> Result<Self, Error> {
959        Ok(Self::new(parent, tag))
960    }
961
962    fn unchecked_into_parent(self) -> P {
963        self.parent
964    }
965}
966
967/// A builder for an optional TLV type.
968pub struct OptionalBuilder<P, T> {
969    parent: P,
970    tag: TLVTag,
971    _t: PhantomData<fn() -> T>,
972}
973
974impl<P, T> OptionalBuilder<P, T>
975where
976    P: TLVBuilderParent,
977    T: TLVBuilder<P>,
978{
979    /// Create a new optional TLV builder for the given parent and tag.
980    pub fn new(parent: P, tag: &TLVTag) -> Self {
981        Self {
982            parent,
983            tag: tag.clone(),
984            _t: PhantomData,
985        }
986    }
987
988    /// Skip writing the TLV type and return the parent.
989    pub fn none(self) -> P {
990        #[cfg(feature = "defmt")]
991        defmt::debug!("{:?}::optional -> none +", self);
992        #[cfg(feature = "log")]
993        ::log::debug!("{:?}::optional -> none +", self);
994
995        self.parent
996    }
997
998    /// Create and return the builder for the non-optional value.
999    pub fn some(self) -> Result<T, Error> {
1000        #[cfg(feature = "defmt")]
1001        defmt::debug!("{:?}::optional -> (some) +", self);
1002        #[cfg(feature = "log")]
1003        ::log::debug!("{:?}::optional -> (some) +", self);
1004
1005        T::new(self.parent, &self.tag)
1006    }
1007
1008    /// If `condition` is `true`, call the closure with the non-optional builder.
1009    /// If `condition` is `false`, return the parent.
1010    pub fn with_some_if<F>(self, condition: bool, f: F) -> Result<P, Error>
1011    where
1012        F: FnOnce(T) -> Result<P, Error>,
1013    {
1014        if condition {
1015            f(self.some()?)
1016        } else {
1017            Ok(self.none())
1018        }
1019    }
1020
1021    /// If the input is `Some`, call the closure with the non-optional builder and the input.
1022    /// If the input is `None`, return the parent.
1023    pub fn with_some<I, F>(self, input: Option<I>, f: F) -> Result<P, Error>
1024    where
1025        F: FnOnce(&I, T) -> Result<P, Error>,
1026    {
1027        if let Some(input) = input.as_ref() {
1028            f(input, self.some()?)
1029        } else {
1030            Ok(self.none())
1031        }
1032    }
1033}
1034
1035impl<P, T> core::fmt::Debug for OptionalBuilder<P, T>
1036where
1037    P: core::fmt::Debug,
1038{
1039    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1040        write!(f, "{:?}", self.parent)
1041    }
1042}
1043
1044#[cfg(feature = "defmt")]
1045impl<P, T> defmt::Format for OptionalBuilder<P, T>
1046where
1047    P: defmt::Format,
1048{
1049    fn format(&self, f: defmt::Formatter<'_>) {
1050        defmt::write!(f, "{:?}", self.parent)
1051    }
1052}
1053
1054impl<P, T> TLVBuilderParent for OptionalBuilder<P, T>
1055where
1056    P: TLVBuilderParent,
1057{
1058    type Write = P::Write;
1059
1060    fn writer(&mut self) -> &mut Self::Write {
1061        self.parent.writer()
1062    }
1063}
1064
1065impl<P, T> TLVBuilder<P> for OptionalBuilder<P, T>
1066where
1067    P: TLVBuilderParent,
1068    T: TLVBuilder<P>,
1069{
1070    fn new(parent: P, tag: &TLVTag) -> Result<Self, Error> {
1071        Ok(Self::new(parent, tag))
1072    }
1073
1074    fn unchecked_into_parent(self) -> P {
1075        self.parent
1076    }
1077}