rs_matter/im/encoding/attr/write_builder.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
18//! Streaming TLV builders for `WriteRequestMessage` and its sub-structures.
19//!
20//! Contrast with the snapshot-style `WriteRequestBuilder` in
21//! `crate::im::client`: that one is a plain data struct that holds a
22//! pre-built `&[AttrData]` and serialises it via `ToTLV` after the
23//! caller has assembled all paths and payloads in some other buffer.
24//!
25//! The builders in *this* module write **directly** into the outbound
26//! `TLVWrite` (typically the exchange's TX `WriteBuf`), field by field,
27//! in the same typestate style the codegen emits for cluster structs.
28//! No intermediate `Vec` of `AttrData`, no separate buffer for the
29//! attribute payload — every byte ends up in the TX buffer exactly
30//! once.
31//!
32//! # Layout
33//!
34//! Per Matter Core spec `WriteRequestMessage` is an
35//! anonymous-tagged struct with four fields:
36//!
37//! | Tag | Field | Type |
38//! |-----|-------------------|---------------------|
39//! | 0 | SuppressResponse | bool? (omit = false)|
40//! | 1 | TimedRequest | bool? (omit = false)|
41//! | 2 | WriteRequests | array[AttrData] |
42//! | 3 | MoreChunkedMessages | bool? |
43//!
44//! `AttrData` (`AttributeDataIB`) is itself a struct with:
45//!
46//! | Tag | Field | Type |
47//! |-----|------------|-------------|
48//! | 0 | DataVersion | u32? |
49//! | 1 | Path | AttrPath |
50//! | 2 | Data | any TLV |
51//!
52//! `AttrPath` (`AttributePathIB`) is a *list* (not a struct, per IM
53//! spec) with optional fields at tags 0..5 (tag_compression, node,
54//! endpoint, cluster, attr, list_index). For attribute writes the
55//! common shape is concrete `(endpoint, cluster, attr)`; the builder
56//! below exposes those three as required, leaving the wildcard /
57//! list-index variants out of the first-cut surface — they can be
58//! added via additional setters when a real use case appears.
59//!
60//! # Usage
61//!
62//! Optional fields are **implicitly skipped** — just don't call their
63//! setter. Later-field methods are available on earlier states, so a
64//! minimal call writes only the mandatory `WriteRequests` array:
65//!
66//! ```ignore
67//! exchange.send_with(|_, wb| {
68//! let parent = TLVWriteParent::new("WriteRequest", wb);
69//! WriteReqBuilder::new(parent)?
70//! // SuppressResponse + TimedRequest implicitly skipped:
71//! .write_requests()?
72//! .push()?
73//! // DataVersion implicitly skipped:
74//! .path(1, 0x0006 /* OnOff */, 0x4001 /* OnTime */)?
75//! .data(|w| 60u16.to_tlv(&TLVTag::Context(2), w))?
76//! .end()?
77//! .end()?
78//! // MoreChunkedMessages implicitly skipped:
79//! .end()?;
80//! Ok(Some(OpCode::WriteRequest.into()))
81//! }).await
82//! ```
83//!
84//! To include an optional field, just call its setter — that locks
85//! out earlier-state alternatives via the typestate, so call order
86//! still matches the spec field order:
87//!
88//! ```ignore
89//! WriteReqBuilder::new(parent)?
90//! .timed_request(true)? // SuppressResponse skipped
91//! .write_requests()?
92//! .push()?
93//! .data_version(42)? // optimistic-concurrency write
94//! .path(1, 0x001F /* ACL */, 0x0000 /* ACL */)?
95//! .data(|w| acl_value.to_tlv(&TLVTag::Context(2), w))?
96//! .end()?
97//! .end()?
98//! .more_chunks(true)? // explicit chunked write
99//! .end()?
100//! ```
101
102use core::marker::PhantomData;
103
104use crate::error::Error;
105use crate::im::encoding::{AttrId, ClusterId, EndptId};
106use crate::im::{AttrDataTag, AttrPathTag, WriteReqTag, IM_REVISION};
107use crate::tlv::{TLVBuilder, TLVBuilderParent, TLVTag, TLVWrite};
108
109/// Streaming builder for a `WriteRequestMessage`. Type-state-tagged
110/// so the compiler enforces in-order field writes. Optional fields
111/// are **implicitly skipped** by simply not calling their setter —
112/// later-field setters are available on all earlier states.
113///
114/// Field-state values (the state *after* each named field has been
115/// written or implicitly skipped):
116/// - `0`: nothing written yet
117/// - `1`: past `SuppressResponse` (written or skipped)
118/// - `2`: past `TimedRequest`
119/// - `3`: past `WriteRequests` array (closed)
120/// - `4`: past `MoreChunkedMessages`
121/// - `5`: past `InteractionModelRevision` (auto-injected at default
122/// value [`IM_REVISION`] by `end()` if the optional setter wasn't
123/// called)
124///
125/// In practice almost every call is `WriteReqBuilder::new(p)?
126/// .write_requests()? … .end()?` — `SuppressResponse` and
127/// `MoreChunkedMessages` default to absent (= false on the wire),
128/// `TimedRequest` is only set when issuing a timed write.
129pub struct WriteReqBuilder<P, const F: usize = 0> {
130 p: P,
131}
132
133impl<P> WriteReqBuilder<P, 0>
134where
135 P: TLVBuilderParent,
136{
137 /// Begin a new `WriteRequestMessage` — opens a struct at the
138 /// given tag on the parent's writer. For top-level use (the
139 /// usual case) pass `&TLVTag::Anonymous`.
140 pub fn new(mut p: P, tag: &TLVTag) -> Result<Self, Error> {
141 p.writer().start_struct(tag)?;
142 Ok(Self { p })
143 }
144}
145
146impl<P> TLVBuilder<P> for WriteReqBuilder<P, 0>
147where
148 P: TLVBuilderParent,
149{
150 fn new(parent: P, tag: &TLVTag) -> Result<Self, Error> {
151 Self::new(parent, tag)
152 }
153
154 fn unchecked_into_parent(self) -> P {
155 self.p
156 }
157}
158
159// ---------------------------------------------------------------------
160// `suppress_response` — settable from state 0; advances to state 1.
161// ---------------------------------------------------------------------
162impl<P> WriteReqBuilder<P, 0>
163where
164 P: TLVBuilderParent,
165{
166 /// Write the optional `SuppressResponse` field. Omit (don't call)
167 /// to leave the field absent on the wire.
168 pub fn suppress_response(mut self, value: bool) -> Result<WriteReqBuilder<P, 1>, Error> {
169 self.p
170 .writer()
171 .bool(&TLVTag::Context(WriteReqTag::SuppressResponse as u8), value)?;
172 Ok(WriteReqBuilder { p: self.p })
173 }
174}
175
176// ---------------------------------------------------------------------
177// `timed_request` — settable from state 0 or 1; advances to state 2.
178// ---------------------------------------------------------------------
179impl<P> WriteReqBuilder<P, 0>
180where
181 P: TLVBuilderParent,
182{
183 /// Write the optional `TimedRequest` field. Calling this from
184 /// state 0 implicitly skips `SuppressResponse`.
185 pub fn timed_request(self, value: bool) -> Result<WriteReqBuilder<P, 2>, Error> {
186 WriteReqBuilder::<P, 1> { p: self.p }.timed_request(value)
187 }
188}
189
190impl<P> WriteReqBuilder<P, 1>
191where
192 P: TLVBuilderParent,
193{
194 /// Write the optional `TimedRequest` field.
195 pub fn timed_request(mut self, value: bool) -> Result<WriteReqBuilder<P, 2>, Error> {
196 self.p
197 .writer()
198 .bool(&TLVTag::Context(WriteReqTag::TimedRequest as u8), value)?;
199 Ok(WriteReqBuilder { p: self.p })
200 }
201}
202
203// ---------------------------------------------------------------------
204// `write_requests` — required; openable from state 0, 1, or 2.
205// ---------------------------------------------------------------------
206impl<P> WriteReqBuilder<P, 0>
207where
208 P: TLVBuilderParent,
209{
210 /// Open the `WriteRequests` array. Calling from state 0
211 /// implicitly skips both `SuppressResponse` and `TimedRequest`.
212 pub fn write_requests(self) -> Result<AttrDataArrayBuilder<WriteReqBuilder<P, 3>>, Error> {
213 WriteReqBuilder::<P, 2> { p: self.p }.write_requests()
214 }
215}
216
217impl<P> WriteReqBuilder<P, 1>
218where
219 P: TLVBuilderParent,
220{
221 /// Open the `WriteRequests` array, implicitly skipping `TimedRequest`.
222 pub fn write_requests(self) -> Result<AttrDataArrayBuilder<WriteReqBuilder<P, 3>>, Error> {
223 WriteReqBuilder::<P, 2> { p: self.p }.write_requests()
224 }
225}
226
227impl<P> WriteReqBuilder<P, 2>
228where
229 P: TLVBuilderParent,
230{
231 /// Open the `WriteRequests` array. Each `.push()` on the returned
232 /// builder starts one `AttrData` entry; close with `.end()` to
233 /// return to the message builder.
234 pub fn write_requests(self) -> Result<AttrDataArrayBuilder<WriteReqBuilder<P, 3>>, Error> {
235 AttrDataArrayBuilder::new(
236 WriteReqBuilder { p: self.p },
237 &TLVTag::Context(WriteReqTag::WriteRequests as u8),
238 )
239 }
240}
241
242// ---------------------------------------------------------------------
243// `more_chunks` — settable from state 3; advances to state 4.
244// ---------------------------------------------------------------------
245impl<P> WriteReqBuilder<P, 3>
246where
247 P: TLVBuilderParent,
248{
249 /// Write the optional `MoreChunkedMessages` field. Omit (don't
250 /// call — go straight to `.end()`) for single-chunk writes.
251 pub fn more_chunks(mut self, value: bool) -> Result<WriteReqBuilder<P, 4>, Error> {
252 self.p
253 .writer()
254 .bool(&TLVTag::Context(WriteReqTag::MoreChunked as u8), value)?;
255 Ok(WriteReqBuilder { p: self.p })
256 }
257}
258
259// ---------------------------------------------------------------------
260// `end` — closable from state 3 or 4.
261// ---------------------------------------------------------------------
262impl<P> WriteReqBuilder<P, 3>
263where
264 P: TLVBuilderParent,
265{
266 /// Close the message struct, implicitly skipping
267 /// `MoreChunkedMessages`. Returns the parent.
268 pub fn end(self) -> Result<P, Error> {
269 WriteReqBuilder::<P, 4> { p: self.p }.end()
270 }
271}
272
273impl<P> WriteReqBuilder<P, 3>
274where
275 P: TLVBuilderParent,
276{
277 /// Write `InteractionModelRevision`, implicitly skipping
278 /// `MoreChunkedMessages`. This is a typestate skip-shim mirroring
279 /// the pattern PR #447 established for `SuppressResponse` /
280 /// `TimedRequest` on `InvReqBuilder`: callers who don't populate
281 /// the optional preceding field can advance straight to setting
282 /// (or auto-injecting) `InteractionModelRevision` without an
283 /// explicit no-op transition.
284 pub fn interaction_model_revision(self, value: u8) -> Result<WriteReqBuilder<P, 5>, Error> {
285 WriteReqBuilder::<P, 4> { p: self.p }.interaction_model_revision(value)
286 }
287}
288
289impl<P> WriteReqBuilder<P, 4>
290where
291 P: TLVBuilderParent,
292{
293 /// Write the mandatory-on-the-wire `InteractionModelRevision`
294 /// field (Matter Core: value is `13` since Matter
295 /// 1.3, unchanged in 1.4 and 1.5). Optional at the API level —
296 /// omit and `end()` injects [`IM_REVISION`] automatically.
297 pub fn interaction_model_revision(mut self, value: u8) -> Result<WriteReqBuilder<P, 5>, Error> {
298 self.p.writer().u8(
299 &TLVTag::Context(crate::im::encoding::IM_REVISION_TAG),
300 value,
301 )?;
302 Ok(WriteReqBuilder { p: self.p })
303 }
304
305 /// Close the message struct, auto-injecting
306 /// `InteractionModelRevision` at its default value
307 /// [`IM_REVISION`]. Returns the parent.
308 pub fn end(self) -> Result<P, Error> {
309 self.interaction_model_revision(IM_REVISION)?.end()
310 }
311}
312
313impl<P> WriteReqBuilder<P, 5>
314where
315 P: TLVBuilderParent,
316{
317 /// Close the message struct and return the parent.
318 pub fn end(mut self) -> Result<P, Error> {
319 self.p.writer().end_container()?;
320 Ok(self.p)
321 }
322}
323
324// Bridge: the `WriteReqBuilder<P, F>` is itself a parent
325// for sub-builders (the array of AttrData). Forward `writer()` to the
326// inner parent.
327impl<P, const F: usize> TLVBuilderParent for WriteReqBuilder<P, F>
328where
329 P: TLVBuilderParent,
330{
331 type Write = P::Write;
332
333 fn writer(&mut self) -> &mut Self::Write {
334 self.p.writer()
335 }
336}
337
338impl<P, const F: usize> core::fmt::Debug for WriteReqBuilder<P, F>
339where
340 P: core::fmt::Debug,
341{
342 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
343 write!(f, "{:?}::WriteRequestMessage<{}>", self.p, F)
344 }
345}
346
347#[cfg(feature = "defmt")]
348impl<P, const F: usize> defmt::Format for WriteReqBuilder<P, F>
349where
350 P: defmt::Format,
351{
352 fn format(&self, fmt: defmt::Formatter<'_>) {
353 defmt::write!(fmt, "{:?}::WriteRequestMessage<{}>", self.p, F);
354 }
355}
356
357/// Array builder for the `WriteRequests` field. The array is opened
358/// in `write_requests()`; this type provides `.push()` (start one
359/// entry) and `.end()` (close the array, return to the message
360/// builder).
361pub struct AttrDataArrayBuilder<P> {
362 p: P,
363}
364
365impl<P> AttrDataArrayBuilder<P>
366where
367 P: TLVBuilderParent,
368{
369 /// Begin a new `AttrData` array — opens an array at the given
370 /// tag on the parent's writer. Use the [`TLVBuilder`] trait
371 /// constructor for the standard call.
372 pub fn new(mut p: P, tag: &TLVTag) -> Result<Self, Error> {
373 p.writer().start_array(tag)?;
374 Ok(Self { p })
375 }
376
377 /// Start a new `AttrData` entry. The returned [`AttrDataBuilder`]
378 /// terminates with `.end()` which returns this array builder.
379 pub fn push(self) -> Result<AttrDataBuilder<Self, 0>, Error> {
380 // Each AttrData is an anonymous-tagged struct (we're inside an array).
381 AttrDataBuilder::new(self, &TLVTag::Anonymous)
382 }
383
384 /// Close the array and return the message builder.
385 pub fn end(mut self) -> Result<P, Error> {
386 self.p.writer().end_container()?;
387 Ok(self.p)
388 }
389}
390
391impl<P> TLVBuilder<P> for AttrDataArrayBuilder<P>
392where
393 P: TLVBuilderParent,
394{
395 fn new(parent: P, tag: &TLVTag) -> Result<Self, Error> {
396 Self::new(parent, tag)
397 }
398
399 fn unchecked_into_parent(self) -> P {
400 self.p
401 }
402}
403
404impl<P> TLVBuilderParent for AttrDataArrayBuilder<P>
405where
406 P: TLVBuilderParent,
407{
408 type Write = P::Write;
409
410 fn writer(&mut self) -> &mut Self::Write {
411 self.p.writer()
412 }
413}
414
415impl<P> core::fmt::Debug for AttrDataArrayBuilder<P>
416where
417 P: core::fmt::Debug,
418{
419 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
420 write!(f, "{:?}[]", self.p)
421 }
422}
423
424#[cfg(feature = "defmt")]
425impl<P> defmt::Format for AttrDataArrayBuilder<P>
426where
427 P: defmt::Format,
428{
429 fn format(&self, fmt: defmt::Formatter<'_>) {
430 defmt::write!(fmt, "{:?}[]", self.p);
431 }
432}
433
434/// Streaming builder for a single `AttrData` entry inside the
435/// `WriteRequests` array.
436///
437/// Field-state values:
438/// - `0`: nothing written yet
439/// - `1`: `DataVersion` decided
440/// - `2`: `Path` written
441/// - `3`: `Data` written (struct can be closed)
442pub struct AttrDataBuilder<P, const F: usize = 0> {
443 p: P,
444 _f: PhantomData<[(); F]>,
445}
446
447impl<P> AttrDataBuilder<P, 0>
448where
449 P: TLVBuilderParent,
450{
451 /// Begin a new `AttrData` entry — opens a struct at the given
452 /// tag. Use `&TLVTag::Anonymous` when pushed into the
453 /// `WriteRequests` array (the typical case).
454 pub fn new(mut p: P, tag: &TLVTag) -> Result<Self, Error> {
455 p.writer().start_struct(tag)?;
456 Ok(Self { p, _f: PhantomData })
457 }
458}
459
460impl<P> TLVBuilder<P> for AttrDataBuilder<P, 0>
461where
462 P: TLVBuilderParent,
463{
464 fn new(parent: P, tag: &TLVTag) -> Result<Self, Error> {
465 Self::new(parent, tag)
466 }
467
468 fn unchecked_into_parent(self) -> P {
469 self.p
470 }
471}
472
473// ---------------------------------------------------------------------
474// `data_version` — settable from state 0; advances to state 1.
475// ---------------------------------------------------------------------
476impl<P> AttrDataBuilder<P, 0>
477where
478 P: TLVBuilderParent,
479{
480 /// Write the optional `DataVersion` field — used for
481 /// optimistic-concurrency writes that should fail on stale data.
482 /// Omit (don't call) for unconditional writes; subsequent `path*`
483 /// methods are also available on state 0 and implicitly skip
484 /// this field.
485 pub fn data_version(mut self, value: u32) -> Result<AttrDataBuilder<P, 1>, Error> {
486 self.p
487 .writer()
488 .u32(&TLVTag::Context(AttrDataTag::DataVer as u8), value)?;
489 Ok(AttrDataBuilder {
490 p: self.p,
491 _f: PhantomData,
492 })
493 }
494}
495
496// ---------------------------------------------------------------------
497// `path` / `path_from` — required; available from state 0 or 1.
498// ---------------------------------------------------------------------
499impl<P> AttrDataBuilder<P, 0>
500where
501 P: TLVBuilderParent,
502{
503 /// Write the concrete `(endpoint, cluster, attribute)` path,
504 /// implicitly skipping `DataVersion`.
505 pub fn path(
506 self,
507 endpoint: EndptId,
508 cluster: ClusterId,
509 attr: AttrId,
510 ) -> Result<AttrDataBuilder<P, 2>, Error> {
511 AttrDataBuilder::<P, 1> {
512 p: self.p,
513 _f: PhantomData,
514 }
515 .path(endpoint, cluster, attr)
516 }
517
518 /// Write the path from an existing [`crate::im::AttrPath`],
519 /// implicitly skipping `DataVersion`. Used by the
520 /// snapshot→streaming bridge in `ImClient::write`.
521 pub fn path_from(self, path: &crate::im::AttrPath) -> Result<AttrDataBuilder<P, 2>, Error> {
522 AttrDataBuilder::<P, 1> {
523 p: self.p,
524 _f: PhantomData,
525 }
526 .path_from(path)
527 }
528}
529
530impl<P> AttrDataBuilder<P, 1>
531where
532 P: TLVBuilderParent,
533{
534 /// Write the concrete `(endpoint, cluster, attribute)` path. This
535 /// is the typical shape for attribute writes; wildcards aren't
536 /// generally meaningful for writes and aren't exposed here. The
537 /// path is encoded as a *list* (per spec `AttributePathIB`).
538 pub fn path(
539 mut self,
540 endpoint: EndptId,
541 cluster: ClusterId,
542 attr: AttrId,
543 ) -> Result<AttrDataBuilder<P, 2>, Error> {
544 let w = self.p.writer();
545 w.start_list(&TLVTag::Context(AttrDataTag::Path as u8))?;
546 w.u16(&TLVTag::Context(AttrPathTag::Endpoint as u8), endpoint)?;
547 w.u32(&TLVTag::Context(AttrPathTag::Cluster as u8), cluster)?;
548 w.u32(&TLVTag::Context(AttrPathTag::Attribute as u8), attr)?;
549 w.end_container()?;
550 Ok(AttrDataBuilder {
551 p: self.p,
552 _f: PhantomData,
553 })
554 }
555
556 /// Write the path from an existing [`crate::im::AttrPath`]. Used
557 /// by the snapshot→streaming bridge in `ImClient::write` so the
558 /// pre-built `AttrPath` (which may carry wildcards or
559 /// `list_index`) is re-emitted faithfully. New call sites should
560 /// prefer [`Self::path`].
561 pub fn path_from(mut self, path: &crate::im::AttrPath) -> Result<AttrDataBuilder<P, 2>, Error> {
562 use crate::tlv::ToTLV;
563 path.to_tlv(&TLVTag::Context(AttrDataTag::Path as u8), self.p.writer())?;
564 Ok(AttrDataBuilder {
565 p: self.p,
566 _f: PhantomData,
567 })
568 }
569}
570
571impl<P> AttrDataBuilder<P, 2>
572where
573 P: TLVBuilderParent,
574{
575 /// Write the attribute value into the `Data` slot.
576 ///
577 /// Per Matter Core spec `AttributeDataIB.Data` is "any
578 /// TLV value." The closure receives the parent's `TLVWrite` and
579 /// **must** emit exactly one TLV element tagged
580 /// `TLVTag::Context(2)` (i.e. `AttrDataTag::Data`). The IM-level
581 /// builder can't enforce the inner type because the schema lives
582 /// in the attribute the path named, not the IM message; the
583 /// closure body is where the caller asserts the schema match
584 /// (typically by calling a codegen-emitted typed writer).
585 ///
586 /// Idiomatic call patterns:
587 ///
588 /// ```ignore
589 /// // Any `T: ToTLV`:
590 /// .data(|w| 60u16.to_tlv(&TLVTag::Context(AttrDataTag::Data as u8), w))?
591 ///
592 /// // A bool, raw:
593 /// .data(|w| w.bool(&TLVTag::Context(AttrDataTag::Data as u8), true))?
594 ///
595 /// // A codegen-emitted typed helper that already knows the tag:
596 /// .data(|w| on_off::write_on_time_value(60u16, w))?
597 /// ```
598 pub fn data<F>(mut self, f: F) -> Result<AttrDataBuilder<P, 3>, Error>
599 where
600 F: FnOnce(&mut P::Write) -> Result<(), Error>,
601 {
602 f(self.p.writer())?;
603 Ok(AttrDataBuilder {
604 p: self.p,
605 _f: PhantomData,
606 })
607 }
608
609 /// Open the `Data` slot as a typed sub-builder.
610 ///
611 /// Closure-free counterpart to [`data`](Self::data) — hand back
612 /// the codegen-emitted typed value builder for the attribute,
613 /// already opened at `AttrDataTag::Data`. The caller fills the
614 /// value, then calls `.end()` on the sub-builder; that close
615 /// writes `Data`'s closing tag (for struct/array-valued attrs)
616 /// and yields an [`AttrDataBuilder<P, 3>`]. The caller then
617 /// `.end()`s once more to close the `AttrData` entry struct
618 /// (the "double-end" pattern of the IM-client glue).
619 ///
620 /// Useful for struct- or array-valued attributes (e.g. ACL
621 /// entries). For scalars prefer the closure-based [`data`].
622 ///
623 /// Soundness of the phantom typestate advance: see
624 /// [`CmdDataBuilder::data_builder`].
625 pub fn data_builder<B>(self) -> Result<B, Error>
626 where
627 B: TLVBuilder<AttrDataBuilder<P, 3>>,
628 {
629 let advanced = AttrDataBuilder {
630 p: self.p,
631 _f: PhantomData,
632 };
633 B::new(advanced, &TLVTag::Context(AttrDataTag::Data as u8))
634 }
635}
636
637impl<P> AttrDataBuilder<P, 3>
638where
639 P: TLVBuilderParent,
640{
641 /// Close the `AttrData` struct and return the array builder so
642 /// the caller can `.push()` another entry or `.end()` the array.
643 pub fn end(mut self) -> Result<P, Error> {
644 self.p.writer().end_container()?;
645 Ok(self.p)
646 }
647}
648
649impl<P, const F: usize> TLVBuilderParent for AttrDataBuilder<P, F>
650where
651 P: TLVBuilderParent,
652{
653 type Write = P::Write;
654
655 fn writer(&mut self) -> &mut Self::Write {
656 self.p.writer()
657 }
658}
659
660impl<P, const F: usize> core::fmt::Debug for AttrDataBuilder<P, F>
661where
662 P: core::fmt::Debug,
663{
664 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
665 write!(f, "{:?}::AttrData<{}>", self.p, F)
666 }
667}
668
669#[cfg(feature = "defmt")]
670impl<P, const F: usize> defmt::Format for AttrDataBuilder<P, F>
671where
672 P: defmt::Format,
673{
674 fn format(&self, fmt: defmt::Formatter<'_>) {
675 defmt::write!(fmt, "{:?}::AttrData<{}>", self.p, F);
676 }
677}