Skip to main content

matter_clusters/gen/
time_synchronization.rs

1//! TimeSynchronization cluster (0x0038).
2//! @generated by `cargo xtask codegen` — do not edit.
3
4#![allow(
5    clippy::all,
6    clippy::pedantic,
7    dead_code,
8    unreachable_pub,
9    unused_imports
10)]
11
12use crate::datatypes::SemanticTagStruct;
13use crate::error::ClusterError;
14use crate::types::Nullable;
15use matter_codec::{ContainerKind, Element, Tag, TlvReader, TlvWriter, Value};
16
17/// Cluster ID.
18pub const CLUSTER_ID: u32 = 0x0038;
19/// Cluster revision.
20pub const CLUSTER_REVISION: u16 = 2;
21
22/// Command IDs (requests and responses).
23pub mod command_id {
24    /// `SetUtcTime` (request).
25    pub const SET_UTC_TIME: u32 = 0x00;
26    /// `SetTrustedTimeSource` (request).
27    pub const SET_TRUSTED_TIME_SOURCE: u32 = 0x01;
28    /// `SetTimeZone` (request).
29    pub const SET_TIME_ZONE: u32 = 0x02;
30    /// `SetTimeZoneResponse` (response).
31    pub const SET_TIME_ZONE_RESPONSE: u32 = 0x03;
32    /// `SetDstOffset` (request).
33    pub const SET_DST_OFFSET: u32 = 0x04;
34    /// `SetDefaultNtp` (request).
35    pub const SET_DEFAULT_NTP: u32 = 0x05;
36}
37
38/// Attribute IDs (cluster-specific).
39pub mod attribute_id {
40    /// `UtcTime`.
41    pub const UTC_TIME: u32 = 0x0000;
42    /// `Granularity`.
43    pub const GRANULARITY: u32 = 0x0001;
44    /// `TimeSource`.
45    pub const TIME_SOURCE: u32 = 0x0002;
46    /// `TrustedTimeSource`.
47    pub const TRUSTED_TIME_SOURCE: u32 = 0x0003;
48    /// `DefaultNtp`.
49    pub const DEFAULT_NTP: u32 = 0x0004;
50    /// `TimeZone`.
51    pub const TIME_ZONE: u32 = 0x0005;
52    /// `DstOffset`.
53    pub const DST_OFFSET: u32 = 0x0006;
54    /// `LocalTime`.
55    pub const LOCAL_TIME: u32 = 0x0007;
56    /// `TimeZoneDatabase`.
57    pub const TIME_ZONE_DATABASE: u32 = 0x0008;
58    /// `NtpServerAvailable`.
59    pub const NTP_SERVER_AVAILABLE: u32 = 0x0009;
60    /// `TimeZoneListMaxSize`.
61    pub const TIME_ZONE_LIST_MAX_SIZE: u32 = 0x000A;
62    /// `DstOffsetListMaxSize`.
63    pub const DST_OFFSET_LIST_MAX_SIZE: u32 = 0x000B;
64    /// `SupportsDnsResolve`.
65    pub const SUPPORTS_DNS_RESOLVE: u32 = 0x000C;
66}
67
68bitflags::bitflags! {
69    /// `TimeSynchronization` feature bits (FeatureMap).
70    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
71    pub struct Feature: u32 {
72        /// TimeZone (TZ).
73        const TZ = 1 << 0;
74        /// NtpClient (NTPC).
75        const NTPC = 1 << 1;
76        /// NtpServer (NTPS).
77        const NTPS = 1 << 2;
78        /// TimeSyncClient (TSC).
79        const TSC = 1 << 3;
80    }
81}
82
83/// `DSTOffsetStruct` struct.
84#[derive(Clone, Debug, PartialEq)]
85pub struct DSTOffsetStruct {
86    /// Field Offset (tag 0).
87    pub offset: i32,
88    /// Field ValidStarting (tag 1).
89    pub valid_starting: u64,
90    /// Field ValidUntil (tag 2).
91    pub valid_until: Nullable<u64>,
92}
93
94/// `FabricScopedTrustedTimeSourceStruct` struct.
95#[derive(Clone, Debug, PartialEq)]
96pub struct FabricScopedTrustedTimeSourceStruct {
97    /// Field NodeId (tag 0).
98    pub node_id: u64,
99    /// Field Endpoint (tag 1).
100    pub endpoint: u16,
101}
102
103/// `GranularityEnum` (enum8).
104#[derive(Copy, Clone, Debug, PartialEq, Eq)]
105pub enum GranularityEnum {
106    /// NoTimeGranularity = 0.
107    NoTimeGranularity,
108    /// MinutesGranularity = 1.
109    MinutesGranularity,
110    /// SecondsGranularity = 2.
111    SecondsGranularity,
112    /// MillisecondsGranularity = 3.
113    MillisecondsGranularity,
114    /// MicrosecondsGranularity = 4.
115    MicrosecondsGranularity,
116    /// A value not known to this codegen revision.
117    Unknown(u8),
118}
119
120impl GranularityEnum {
121    /// Decode from its raw discriminant (unknown → `Unknown`).
122    #[must_use]
123    pub fn from_raw(v: u8) -> Self {
124        match v {
125            0 => Self::NoTimeGranularity,
126            1 => Self::MinutesGranularity,
127            2 => Self::SecondsGranularity,
128            3 => Self::MillisecondsGranularity,
129            4 => Self::MicrosecondsGranularity,
130            other => Self::Unknown(other),
131        }
132    }
133    /// The raw discriminant.
134    #[must_use]
135    pub fn to_raw(self) -> u8 {
136        match self {
137            Self::NoTimeGranularity => 0,
138            Self::MinutesGranularity => 1,
139            Self::SecondsGranularity => 2,
140            Self::MillisecondsGranularity => 3,
141            Self::MicrosecondsGranularity => 4,
142            Self::Unknown(v) => v,
143        }
144    }
145}
146
147/// `StatusCodeEnum` (enum8).
148#[derive(Copy, Clone, Debug, PartialEq, Eq)]
149pub enum StatusCodeEnum {
150    /// TimeNotAccepted = 2.
151    TimeNotAccepted,
152    /// A value not known to this codegen revision.
153    Unknown(u8),
154}
155
156impl StatusCodeEnum {
157    /// Decode from its raw discriminant (unknown → `Unknown`).
158    #[must_use]
159    pub fn from_raw(v: u8) -> Self {
160        match v {
161            2 => Self::TimeNotAccepted,
162            other => Self::Unknown(other),
163        }
164    }
165    /// The raw discriminant.
166    #[must_use]
167    pub fn to_raw(self) -> u8 {
168        match self {
169            Self::TimeNotAccepted => 2,
170            Self::Unknown(v) => v,
171        }
172    }
173}
174
175/// `TimeSourceEnum` (enum8).
176#[derive(Copy, Clone, Debug, PartialEq, Eq)]
177pub enum TimeSourceEnum {
178    /// None = 0.
179    None,
180    /// Unknown = 1.
181    Unknown,
182    /// Admin = 2.
183    Admin,
184    /// NodeTimeCluster = 3.
185    NodeTimeCluster,
186    /// NonMatterSntp = 4.
187    NonMatterSntp,
188    /// NonMatterNtp = 5.
189    NonMatterNtp,
190    /// MatterSntp = 6.
191    MatterSntp,
192    /// MatterNtp = 7.
193    MatterNtp,
194    /// MixedNtp = 8.
195    MixedNtp,
196    /// NonMatterSntpnts = 9.
197    NonMatterSntpnts,
198    /// NonMatterNtpnts = 10.
199    NonMatterNtpnts,
200    /// MatterSntpnts = 11.
201    MatterSntpnts,
202    /// MatterNtpnts = 12.
203    MatterNtpnts,
204    /// MixedNtpnts = 13.
205    MixedNtpnts,
206    /// CloudSource = 14.
207    CloudSource,
208    /// Ptp = 15.
209    Ptp,
210    /// Gnss = 16.
211    Gnss,
212    /// A value not known to this codegen revision.
213    Unrecognized(u8),
214}
215
216impl TimeSourceEnum {
217    /// Decode from its raw discriminant (unknown → `Unrecognized`).
218    #[must_use]
219    pub fn from_raw(v: u8) -> Self {
220        match v {
221            0 => Self::None,
222            1 => Self::Unknown,
223            2 => Self::Admin,
224            3 => Self::NodeTimeCluster,
225            4 => Self::NonMatterSntp,
226            5 => Self::NonMatterNtp,
227            6 => Self::MatterSntp,
228            7 => Self::MatterNtp,
229            8 => Self::MixedNtp,
230            9 => Self::NonMatterSntpnts,
231            10 => Self::NonMatterNtpnts,
232            11 => Self::MatterSntpnts,
233            12 => Self::MatterNtpnts,
234            13 => Self::MixedNtpnts,
235            14 => Self::CloudSource,
236            15 => Self::Ptp,
237            16 => Self::Gnss,
238            other => Self::Unrecognized(other),
239        }
240    }
241    /// The raw discriminant.
242    #[must_use]
243    pub fn to_raw(self) -> u8 {
244        match self {
245            Self::None => 0,
246            Self::Unknown => 1,
247            Self::Admin => 2,
248            Self::NodeTimeCluster => 3,
249            Self::NonMatterSntp => 4,
250            Self::NonMatterNtp => 5,
251            Self::MatterSntp => 6,
252            Self::MatterNtp => 7,
253            Self::MixedNtp => 8,
254            Self::NonMatterSntpnts => 9,
255            Self::NonMatterNtpnts => 10,
256            Self::MatterSntpnts => 11,
257            Self::MatterNtpnts => 12,
258            Self::MixedNtpnts => 13,
259            Self::CloudSource => 14,
260            Self::Ptp => 15,
261            Self::Gnss => 16,
262            Self::Unrecognized(v) => v,
263        }
264    }
265}
266
267/// `TimeZoneDatabaseEnum` (enum8).
268#[derive(Copy, Clone, Debug, PartialEq, Eq)]
269pub enum TimeZoneDatabaseEnum {
270    /// Full = 0.
271    Full,
272    /// Partial = 1.
273    Partial,
274    /// None = 2.
275    None,
276    /// A value not known to this codegen revision.
277    Unknown(u8),
278}
279
280impl TimeZoneDatabaseEnum {
281    /// Decode from its raw discriminant (unknown → `Unknown`).
282    #[must_use]
283    pub fn from_raw(v: u8) -> Self {
284        match v {
285            0 => Self::Full,
286            1 => Self::Partial,
287            2 => Self::None,
288            other => Self::Unknown(other),
289        }
290    }
291    /// The raw discriminant.
292    #[must_use]
293    pub fn to_raw(self) -> u8 {
294        match self {
295            Self::Full => 0,
296            Self::Partial => 1,
297            Self::None => 2,
298            Self::Unknown(v) => v,
299        }
300    }
301}
302
303/// `TimeZoneStruct` struct.
304#[derive(Clone, Debug, PartialEq)]
305pub struct TimeZoneStruct {
306    /// Field Offset (tag 0).
307    pub offset: i32,
308    /// Field ValidAt (tag 1).
309    pub valid_at: u64,
310    /// Field Name (tag 2).
311    pub name: Option<String>,
312}
313
314/// `TrustedTimeSourceStruct` struct.
315#[derive(Clone, Debug, PartialEq)]
316#[non_exhaustive]
317pub struct TrustedTimeSourceStruct {
318    /// Field FabricIndex (tag 0).
319    pub fabric_index: u8,
320    /// Field NodeId (tag 1).
321    pub node_id: u64,
322    /// Field Endpoint (tag 2).
323    pub endpoint: u16,
324}
325
326impl DSTOffsetStruct {
327    /// Decode the fields of an already-opened anonymous structure
328    /// (reader positioned after the struct start; consumes to its end).
329    ///
330    /// # Errors
331    /// Returns [`ClusterError`] on a malformed structure or missing required field.
332    pub fn decode_from(r: &mut TlvReader<'_>) -> Result<Self, ClusterError> {
333        let mut f_offset: Option<i32> = None;
334        let mut f_valid_starting: Option<u64> = None;
335        let mut f_valid_until: Option<Nullable<u64>> = None;
336        loop {
337            match r.next()? {
338                Some(Element::ContainerEnd) => break,
339                Some(Element::Scalar {
340                    tag: Tag::Context(0),
341                    value: Value::Int(v),
342                }) => {
343                    f_offset =
344                        Some(i32::try_from(v).map_err(|_| ClusterError::InvalidLength("Offset"))?)
345                }
346                Some(Element::Scalar {
347                    tag: Tag::Context(1),
348                    value: Value::Uint(v),
349                }) => {
350                    f_valid_starting = Some(
351                        u64::try_from(v)
352                            .map_err(|_| ClusterError::InvalidLength("ValidStarting"))?,
353                    )
354                }
355                Some(Element::Scalar {
356                    tag: Tag::Context(2),
357                    value: Value::Null,
358                }) => f_valid_until = Some(Nullable::Null),
359                Some(Element::Scalar {
360                    tag: Tag::Context(2),
361                    value: Value::Uint(v),
362                }) => {
363                    f_valid_until = Some(Nullable::Value(
364                        u64::try_from(v).map_err(|_| ClusterError::InvalidLength("ValidUntil"))?,
365                    ))
366                }
367                None => return Err(ClusterError::Tlv(matter_codec::Error::UnclosedContainer)),
368                Some(Element::ContainerStart { .. }) => r.skip_container()?,
369                Some(_) => {} // unknown/future scalar — skip
370            }
371        }
372        Ok(Self {
373            offset: f_offset.ok_or(ClusterError::MissingField("Offset"))?,
374            valid_starting: f_valid_starting.ok_or(ClusterError::MissingField("ValidStarting"))?,
375            valid_until: f_valid_until.ok_or(ClusterError::MissingField("ValidUntil"))?,
376        })
377    }
378    /// Decode from a standalone anonymous TLV structure.
379    ///
380    /// # Errors
381    /// Returns [`ClusterError`] if the bytes are not an anonymous structure or a field is malformed.
382    pub fn decode(tlv: &[u8]) -> Result<Self, ClusterError> {
383        let mut r = TlvReader::new(tlv);
384        match r.next()? {
385            Some(Element::ContainerStart {
386                kind: ContainerKind::Structure,
387                ..
388            }) => {}
389            _ => {
390                return Err(ClusterError::UnexpectedType {
391                    context: "DSTOffsetStruct",
392                })
393            }
394        }
395        Self::decode_from(&mut r)
396    }
397    /// Write this struct's fields into an already-open container.
398    #[allow(clippy::expect_used)] // Vec-backed TlvWriter is infallible.
399    pub fn write_fields(&self, w: &mut TlvWriter<'_>) {
400        w.put_int(Tag::Context(0), i64::from(self.offset))
401            .expect("infallible: vec writer");
402        w.put_uint(Tag::Context(1), u64::from(self.valid_starting))
403            .expect("infallible: vec writer");
404        match &self.valid_until {
405            Nullable::Null => w.put_null(Tag::Context(2)).expect("infallible: vec writer"),
406            Nullable::Value(valid_until) => {
407                w.put_uint(Tag::Context(2), u64::from(*valid_until))
408                    .expect("infallible: vec writer");
409            }
410        }
411    }
412    /// Encode as a standalone anonymous TLV structure.
413    #[must_use]
414    #[allow(clippy::expect_used)] // Vec-backed TlvWriter is infallible.
415    pub fn encode(&self) -> Vec<u8> {
416        let mut buf = Vec::new();
417        let mut w = TlvWriter::new(&mut buf);
418        w.start_structure(Tag::Anonymous)
419            .expect("infallible: vec writer");
420        self.write_fields(&mut w);
421        w.end_container().expect("infallible: vec writer");
422        buf
423    }
424}
425
426impl FabricScopedTrustedTimeSourceStruct {
427    /// Decode the fields of an already-opened anonymous structure
428    /// (reader positioned after the struct start; consumes to its end).
429    ///
430    /// # Errors
431    /// Returns [`ClusterError`] on a malformed structure or missing required field.
432    pub fn decode_from(r: &mut TlvReader<'_>) -> Result<Self, ClusterError> {
433        let mut f_node_id: Option<u64> = None;
434        let mut f_endpoint: Option<u16> = None;
435        loop {
436            match r.next()? {
437                Some(Element::ContainerEnd) => break,
438                Some(Element::Scalar {
439                    tag: Tag::Context(0),
440                    value: Value::Uint(v),
441                }) => {
442                    f_node_id =
443                        Some(u64::try_from(v).map_err(|_| ClusterError::InvalidLength("NodeId"))?)
444                }
445                Some(Element::Scalar {
446                    tag: Tag::Context(1),
447                    value: Value::Uint(v),
448                }) => {
449                    f_endpoint = Some(
450                        u16::try_from(v).map_err(|_| ClusterError::InvalidLength("Endpoint"))?,
451                    )
452                }
453                None => return Err(ClusterError::Tlv(matter_codec::Error::UnclosedContainer)),
454                Some(Element::ContainerStart { .. }) => r.skip_container()?,
455                Some(_) => {} // unknown/future scalar — skip
456            }
457        }
458        Ok(Self {
459            node_id: f_node_id.ok_or(ClusterError::MissingField("NodeId"))?,
460            endpoint: f_endpoint.ok_or(ClusterError::MissingField("Endpoint"))?,
461        })
462    }
463    /// Decode from a standalone anonymous TLV structure.
464    ///
465    /// # Errors
466    /// Returns [`ClusterError`] if the bytes are not an anonymous structure or a field is malformed.
467    pub fn decode(tlv: &[u8]) -> Result<Self, ClusterError> {
468        let mut r = TlvReader::new(tlv);
469        match r.next()? {
470            Some(Element::ContainerStart {
471                kind: ContainerKind::Structure,
472                ..
473            }) => {}
474            _ => {
475                return Err(ClusterError::UnexpectedType {
476                    context: "FabricScopedTrustedTimeSourceStruct",
477                })
478            }
479        }
480        Self::decode_from(&mut r)
481    }
482    /// Write this struct's fields into an already-open container.
483    #[allow(clippy::expect_used)] // Vec-backed TlvWriter is infallible.
484    pub fn write_fields(&self, w: &mut TlvWriter<'_>) {
485        w.put_uint(Tag::Context(0), u64::from(self.node_id))
486            .expect("infallible: vec writer");
487        w.put_uint(Tag::Context(1), u64::from(self.endpoint))
488            .expect("infallible: vec writer");
489    }
490    /// Encode as a standalone anonymous TLV structure.
491    #[must_use]
492    #[allow(clippy::expect_used)] // Vec-backed TlvWriter is infallible.
493    pub fn encode(&self) -> Vec<u8> {
494        let mut buf = Vec::new();
495        let mut w = TlvWriter::new(&mut buf);
496        w.start_structure(Tag::Anonymous)
497            .expect("infallible: vec writer");
498        self.write_fields(&mut w);
499        w.end_container().expect("infallible: vec writer");
500        buf
501    }
502}
503
504impl TimeZoneStruct {
505    /// Decode the fields of an already-opened anonymous structure
506    /// (reader positioned after the struct start; consumes to its end).
507    ///
508    /// # Errors
509    /// Returns [`ClusterError`] on a malformed structure or missing required field.
510    pub fn decode_from(r: &mut TlvReader<'_>) -> Result<Self, ClusterError> {
511        let mut f_offset: Option<i32> = None;
512        let mut f_valid_at: Option<u64> = None;
513        let mut f_name: Option<String> = None;
514        loop {
515            match r.next()? {
516                Some(Element::ContainerEnd) => break,
517                Some(Element::Scalar {
518                    tag: Tag::Context(0),
519                    value: Value::Int(v),
520                }) => {
521                    f_offset =
522                        Some(i32::try_from(v).map_err(|_| ClusterError::InvalidLength("Offset"))?)
523                }
524                Some(Element::Scalar {
525                    tag: Tag::Context(1),
526                    value: Value::Uint(v),
527                }) => {
528                    f_valid_at =
529                        Some(u64::try_from(v).map_err(|_| ClusterError::InvalidLength("ValidAt"))?)
530                }
531                Some(Element::Scalar {
532                    tag: Tag::Context(2),
533                    value: Value::Utf8(v),
534                }) => f_name = Some(v),
535                None => return Err(ClusterError::Tlv(matter_codec::Error::UnclosedContainer)),
536                Some(Element::ContainerStart { .. }) => r.skip_container()?,
537                Some(_) => {} // unknown/future scalar — skip
538            }
539        }
540        Ok(Self {
541            offset: f_offset.ok_or(ClusterError::MissingField("Offset"))?,
542            valid_at: f_valid_at.ok_or(ClusterError::MissingField("ValidAt"))?,
543            name: f_name,
544        })
545    }
546    /// Decode from a standalone anonymous TLV structure.
547    ///
548    /// # Errors
549    /// Returns [`ClusterError`] if the bytes are not an anonymous structure or a field is malformed.
550    pub fn decode(tlv: &[u8]) -> Result<Self, ClusterError> {
551        let mut r = TlvReader::new(tlv);
552        match r.next()? {
553            Some(Element::ContainerStart {
554                kind: ContainerKind::Structure,
555                ..
556            }) => {}
557            _ => {
558                return Err(ClusterError::UnexpectedType {
559                    context: "TimeZoneStruct",
560                })
561            }
562        }
563        Self::decode_from(&mut r)
564    }
565    /// Write this struct's fields into an already-open container.
566    #[allow(clippy::expect_used)] // Vec-backed TlvWriter is infallible.
567    pub fn write_fields(&self, w: &mut TlvWriter<'_>) {
568        w.put_int(Tag::Context(0), i64::from(self.offset))
569            .expect("infallible: vec writer");
570        w.put_uint(Tag::Context(1), u64::from(self.valid_at))
571            .expect("infallible: vec writer");
572        if let Some(name) = &self.name {
573            w.put_utf8(Tag::Context(2), &*name)
574                .expect("infallible: vec writer");
575        }
576    }
577    /// Encode as a standalone anonymous TLV structure.
578    #[must_use]
579    #[allow(clippy::expect_used)] // Vec-backed TlvWriter is infallible.
580    pub fn encode(&self) -> Vec<u8> {
581        let mut buf = Vec::new();
582        let mut w = TlvWriter::new(&mut buf);
583        w.start_structure(Tag::Anonymous)
584            .expect("infallible: vec writer");
585        self.write_fields(&mut w);
586        w.end_container().expect("infallible: vec writer");
587        buf
588    }
589}
590
591impl TrustedTimeSourceStruct {
592    /// Decode the fields of an already-opened anonymous structure
593    /// (reader positioned after the struct start; consumes to its end).
594    ///
595    /// # Errors
596    /// Returns [`ClusterError`] on a malformed structure or missing required field.
597    pub fn decode_from(r: &mut TlvReader<'_>) -> Result<Self, ClusterError> {
598        let mut f_fabric_index: Option<u8> = None;
599        let mut f_node_id: Option<u64> = None;
600        let mut f_endpoint: Option<u16> = None;
601        loop {
602            match r.next()? {
603                Some(Element::ContainerEnd) => break,
604                Some(Element::Scalar {
605                    tag: Tag::Context(0),
606                    value: Value::Uint(v),
607                }) => {
608                    f_fabric_index = Some(
609                        u8::try_from(v).map_err(|_| ClusterError::InvalidLength("FabricIndex"))?,
610                    )
611                }
612                Some(Element::Scalar {
613                    tag: Tag::Context(1),
614                    value: Value::Uint(v),
615                }) => {
616                    f_node_id =
617                        Some(u64::try_from(v).map_err(|_| ClusterError::InvalidLength("NodeId"))?)
618                }
619                Some(Element::Scalar {
620                    tag: Tag::Context(2),
621                    value: Value::Uint(v),
622                }) => {
623                    f_endpoint = Some(
624                        u16::try_from(v).map_err(|_| ClusterError::InvalidLength("Endpoint"))?,
625                    )
626                }
627                None => return Err(ClusterError::Tlv(matter_codec::Error::UnclosedContainer)),
628                Some(Element::ContainerStart { .. }) => r.skip_container()?,
629                Some(_) => {} // unknown/future scalar — skip
630            }
631        }
632        Ok(Self {
633            fabric_index: f_fabric_index.ok_or(ClusterError::MissingField("FabricIndex"))?,
634            node_id: f_node_id.ok_or(ClusterError::MissingField("NodeId"))?,
635            endpoint: f_endpoint.ok_or(ClusterError::MissingField("Endpoint"))?,
636        })
637    }
638    /// Decode from a standalone anonymous TLV structure.
639    ///
640    /// # Errors
641    /// Returns [`ClusterError`] if the bytes are not an anonymous structure or a field is malformed.
642    pub fn decode(tlv: &[u8]) -> Result<Self, ClusterError> {
643        let mut r = TlvReader::new(tlv);
644        match r.next()? {
645            Some(Element::ContainerStart {
646                kind: ContainerKind::Structure,
647                ..
648            }) => {}
649            _ => {
650                return Err(ClusterError::UnexpectedType {
651                    context: "TrustedTimeSourceStruct",
652                })
653            }
654        }
655        Self::decode_from(&mut r)
656    }
657    /// Write this struct's fields into an already-open container.
658    #[allow(clippy::expect_used)] // Vec-backed TlvWriter is infallible.
659    pub fn write_fields(&self, w: &mut TlvWriter<'_>) {
660        w.put_uint(Tag::Context(0), u64::from(self.fabric_index))
661            .expect("infallible: vec writer");
662        w.put_uint(Tag::Context(1), u64::from(self.node_id))
663            .expect("infallible: vec writer");
664        w.put_uint(Tag::Context(2), u64::from(self.endpoint))
665            .expect("infallible: vec writer");
666    }
667    /// Encode as a standalone anonymous TLV structure.
668    #[must_use]
669    #[allow(clippy::expect_used)] // Vec-backed TlvWriter is infallible.
670    pub fn encode(&self) -> Vec<u8> {
671        let mut buf = Vec::new();
672        let mut w = TlvWriter::new(&mut buf);
673        w.start_structure(Tag::Anonymous)
674            .expect("infallible: vec writer");
675        self.write_fields(&mut w);
676        w.end_container().expect("infallible: vec writer");
677        buf
678    }
679}
680
681/// Decode the `UtcTime` attribute value.
682///
683/// # Errors
684/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
685pub fn decode_utc_time(tlv: &[u8]) -> Result<Nullable<u64>, ClusterError> {
686    let mut r = TlvReader::new(tlv);
687    match r.next()? {
688        Some(Element::Scalar {
689            value: Value::Null, ..
690        }) => Ok(Nullable::Null),
691        Some(Element::Scalar {
692            value: Value::Uint(v),
693            ..
694        }) => Ok(Nullable::Value(
695            u64::try_from(v).map_err(|_| ClusterError::InvalidLength("UtcTime"))?,
696        )),
697        _ => Err(ClusterError::UnexpectedType { context: "UtcTime" }),
698    }
699}
700
701/// Decode the `Granularity` attribute value.
702///
703/// # Errors
704/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
705pub fn decode_granularity(tlv: &[u8]) -> Result<GranularityEnum, ClusterError> {
706    let mut r = TlvReader::new(tlv);
707    match r.next()? {
708        Some(Element::Scalar {
709            value: Value::Uint(v),
710            ..
711        }) => Ok(GranularityEnum::from_raw(
712            u8::try_from(v).map_err(|_| ClusterError::InvalidLength("Granularity"))?,
713        )),
714        _ => Err(ClusterError::UnexpectedType {
715            context: "Granularity",
716        }),
717    }
718}
719
720/// Decode the `TimeSource` attribute value.
721///
722/// # Errors
723/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
724pub fn decode_time_source(tlv: &[u8]) -> Result<TimeSourceEnum, ClusterError> {
725    let mut r = TlvReader::new(tlv);
726    match r.next()? {
727        Some(Element::Scalar {
728            value: Value::Uint(v),
729            ..
730        }) => Ok(TimeSourceEnum::from_raw(
731            u8::try_from(v).map_err(|_| ClusterError::InvalidLength("TimeSource"))?,
732        )),
733        _ => Err(ClusterError::UnexpectedType {
734            context: "TimeSource",
735        }),
736    }
737}
738
739/// Decode the `TrustedTimeSource` attribute value.
740///
741/// # Errors
742/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
743pub fn decode_trusted_time_source(
744    tlv: &[u8],
745) -> Result<Nullable<TrustedTimeSourceStruct>, ClusterError> {
746    let mut r = TlvReader::new(tlv);
747    match r.next()? {
748        Some(Element::Scalar {
749            value: Value::Null, ..
750        }) => Ok(Nullable::Null),
751        Some(Element::ContainerStart {
752            kind: ContainerKind::Structure,
753            ..
754        }) => Ok(Nullable::Value(TrustedTimeSourceStruct::decode_from(
755            &mut r,
756        )?)),
757        _ => Err(ClusterError::UnexpectedType {
758            context: "TrustedTimeSource",
759        }),
760    }
761}
762
763/// Decode the `DefaultNtp` attribute value.
764///
765/// # Errors
766/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
767pub fn decode_default_ntp(tlv: &[u8]) -> Result<Nullable<String>, ClusterError> {
768    let mut r = TlvReader::new(tlv);
769    match r.next()? {
770        Some(Element::Scalar {
771            value: Value::Null, ..
772        }) => Ok(Nullable::Null),
773        Some(Element::Scalar {
774            value: Value::Utf8(v),
775            ..
776        }) => Ok(Nullable::Value(v)),
777        _ => Err(ClusterError::UnexpectedType {
778            context: "DefaultNtp",
779        }),
780    }
781}
782
783/// Decode the `TimeZone` attribute value.
784///
785/// # Errors
786/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
787pub fn decode_time_zone(tlv: &[u8]) -> Result<Vec<TimeZoneStruct>, ClusterError> {
788    let mut r = TlvReader::new(tlv);
789    match r.next()? {
790        Some(Element::ContainerStart {
791            kind: ContainerKind::Array,
792            ..
793        }) => {}
794        _ => {
795            return Err(ClusterError::UnexpectedType {
796                context: "TimeZone",
797            })
798        }
799    }
800    let r = &mut r;
801    let mut out = Vec::new();
802    loop {
803        match r.next()? {
804            Some(Element::ContainerEnd) => break,
805            Some(Element::ContainerStart {
806                kind: ContainerKind::Structure,
807                ..
808            }) => {
809                out.push(TimeZoneStruct::decode_from(r)?);
810            }
811            None => return Err(ClusterError::Tlv(matter_codec::Error::UnclosedContainer)),
812            Some(Element::ContainerStart { .. }) => r.skip_container()?,
813            Some(_) => {} // skip unknown scalar
814        }
815    }
816    Ok(out)
817}
818
819/// Decode the `DstOffset` attribute value.
820///
821/// # Errors
822/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
823pub fn decode_dst_offset(tlv: &[u8]) -> Result<Vec<DSTOffsetStruct>, ClusterError> {
824    let mut r = TlvReader::new(tlv);
825    match r.next()? {
826        Some(Element::ContainerStart {
827            kind: ContainerKind::Array,
828            ..
829        }) => {}
830        _ => {
831            return Err(ClusterError::UnexpectedType {
832                context: "DstOffset",
833            })
834        }
835    }
836    let r = &mut r;
837    let mut out = Vec::new();
838    loop {
839        match r.next()? {
840            Some(Element::ContainerEnd) => break,
841            Some(Element::ContainerStart {
842                kind: ContainerKind::Structure,
843                ..
844            }) => {
845                out.push(DSTOffsetStruct::decode_from(r)?);
846            }
847            None => return Err(ClusterError::Tlv(matter_codec::Error::UnclosedContainer)),
848            Some(Element::ContainerStart { .. }) => r.skip_container()?,
849            Some(_) => {} // skip unknown scalar
850        }
851    }
852    Ok(out)
853}
854
855/// Decode the `LocalTime` attribute value.
856///
857/// # Errors
858/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
859pub fn decode_local_time(tlv: &[u8]) -> Result<Nullable<u64>, ClusterError> {
860    let mut r = TlvReader::new(tlv);
861    match r.next()? {
862        Some(Element::Scalar {
863            value: Value::Null, ..
864        }) => Ok(Nullable::Null),
865        Some(Element::Scalar {
866            value: Value::Uint(v),
867            ..
868        }) => Ok(Nullable::Value(
869            u64::try_from(v).map_err(|_| ClusterError::InvalidLength("LocalTime"))?,
870        )),
871        _ => Err(ClusterError::UnexpectedType {
872            context: "LocalTime",
873        }),
874    }
875}
876
877/// Decode the `TimeZoneDatabase` attribute value.
878///
879/// # Errors
880/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
881pub fn decode_time_zone_database(tlv: &[u8]) -> Result<TimeZoneDatabaseEnum, ClusterError> {
882    let mut r = TlvReader::new(tlv);
883    match r.next()? {
884        Some(Element::Scalar {
885            value: Value::Uint(v),
886            ..
887        }) => Ok(TimeZoneDatabaseEnum::from_raw(
888            u8::try_from(v).map_err(|_| ClusterError::InvalidLength("TimeZoneDatabase"))?,
889        )),
890        _ => Err(ClusterError::UnexpectedType {
891            context: "TimeZoneDatabase",
892        }),
893    }
894}
895
896/// Decode the `NtpServerAvailable` attribute value.
897///
898/// # Errors
899/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
900pub fn decode_ntp_server_available(tlv: &[u8]) -> Result<bool, ClusterError> {
901    let mut r = TlvReader::new(tlv);
902    match r.next()? {
903        Some(Element::Scalar {
904            value: Value::Bool(v),
905            ..
906        }) => Ok(v),
907        _ => Err(ClusterError::UnexpectedType {
908            context: "NtpServerAvailable",
909        }),
910    }
911}
912
913/// Decode the `TimeZoneListMaxSize` attribute value.
914///
915/// # Errors
916/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
917pub fn decode_time_zone_list_max_size(tlv: &[u8]) -> Result<u8, ClusterError> {
918    let mut r = TlvReader::new(tlv);
919    match r.next()? {
920        Some(Element::Scalar {
921            value: Value::Uint(v),
922            ..
923        }) => Ok(u8::try_from(v).map_err(|_| ClusterError::InvalidLength("TimeZoneListMaxSize"))?),
924        _ => Err(ClusterError::UnexpectedType {
925            context: "TimeZoneListMaxSize",
926        }),
927    }
928}
929
930/// Decode the `DstOffsetListMaxSize` attribute value.
931///
932/// # Errors
933/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
934pub fn decode_dst_offset_list_max_size(tlv: &[u8]) -> Result<u8, ClusterError> {
935    let mut r = TlvReader::new(tlv);
936    match r.next()? {
937        Some(Element::Scalar {
938            value: Value::Uint(v),
939            ..
940        }) => Ok(u8::try_from(v).map_err(|_| ClusterError::InvalidLength("DstOffsetListMaxSize"))?),
941        _ => Err(ClusterError::UnexpectedType {
942            context: "DstOffsetListMaxSize",
943        }),
944    }
945}
946
947/// Decode the `SupportsDnsResolve` attribute value.
948///
949/// # Errors
950/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
951pub fn decode_supports_dns_resolve(tlv: &[u8]) -> Result<bool, ClusterError> {
952    let mut r = TlvReader::new(tlv);
953    match r.next()? {
954        Some(Element::Scalar {
955            value: Value::Bool(v),
956            ..
957        }) => Ok(v),
958        _ => Err(ClusterError::UnexpectedType {
959            context: "SupportsDnsResolve",
960        }),
961    }
962}
963
964/// Encode the `SetUtcTime` command request payload.
965#[must_use]
966#[allow(clippy::expect_used, clippy::missing_panics_doc)] // Vec-backed TlvWriter is infallible.
967pub fn encode_set_utc_time(
968    utc_time: u64,
969    granularity: GranularityEnum,
970    time_source: Option<TimeSourceEnum>,
971) -> Vec<u8> {
972    let mut buf = Vec::new();
973    let mut w = TlvWriter::new(&mut buf);
974    w.start_structure(Tag::Anonymous)
975        .expect("infallible: vec writer");
976    w.put_uint(Tag::Context(0), u64::from(utc_time))
977        .expect("infallible: vec writer");
978    w.put_uint(Tag::Context(1), u64::from(granularity.to_raw()))
979        .expect("infallible: vec writer");
980    if let Some(time_source) = time_source {
981        w.put_uint(Tag::Context(2), u64::from(time_source.to_raw()))
982            .expect("infallible: vec writer");
983    }
984    w.end_container().expect("infallible: vec writer");
985    buf
986}
987
988/// Encode the `SetTrustedTimeSource` command request payload.
989#[must_use]
990#[allow(clippy::expect_used, clippy::missing_panics_doc)] // Vec-backed TlvWriter is infallible.
991pub fn encode_set_trusted_time_source(
992    trusted_time_source: Nullable<FabricScopedTrustedTimeSourceStruct>,
993) -> Vec<u8> {
994    let mut buf = Vec::new();
995    let mut w = TlvWriter::new(&mut buf);
996    w.start_structure(Tag::Anonymous)
997        .expect("infallible: vec writer");
998    match &trusted_time_source {
999        Nullable::Null => w.put_null(Tag::Context(0)).expect("infallible: vec writer"),
1000        Nullable::Value(trusted_time_source) => {
1001            w.start_structure(Tag::Context(0))
1002                .expect("infallible: vec writer");
1003            trusted_time_source.write_fields(&mut w);
1004            w.end_container().expect("infallible: vec writer");
1005        }
1006    }
1007    w.end_container().expect("infallible: vec writer");
1008    buf
1009}
1010
1011/// Encode the `SetTimeZone` command request payload.
1012#[must_use]
1013#[allow(clippy::expect_used, clippy::missing_panics_doc)] // Vec-backed TlvWriter is infallible.
1014pub fn encode_set_time_zone(time_zone: &Vec<TimeZoneStruct>) -> Vec<u8> {
1015    let mut buf = Vec::new();
1016    let mut w = TlvWriter::new(&mut buf);
1017    w.start_structure(Tag::Anonymous)
1018        .expect("infallible: vec writer");
1019    w.start_array(Tag::Context(0))
1020        .expect("infallible: vec writer");
1021    for el in time_zone.iter() {
1022        w.start_structure(Tag::Anonymous)
1023            .expect("infallible: vec writer");
1024        el.write_fields(&mut w);
1025        w.end_container().expect("infallible: vec writer");
1026    }
1027    w.end_container().expect("infallible: vec writer");
1028    w.end_container().expect("infallible: vec writer");
1029    buf
1030}
1031
1032/// Decoded `SetTimeZoneResponse` payload.
1033#[derive(Clone, Debug, PartialEq)]
1034#[non_exhaustive]
1035pub struct SetTimeZoneResponse {
1036    /// Field DstOffsetRequired (tag 0).
1037    pub dst_offset_required: bool,
1038}
1039
1040impl SetTimeZoneResponse {
1041    /// Decode the fields of an already-opened anonymous structure
1042    /// (reader positioned after the struct start; consumes to its end).
1043    ///
1044    /// # Errors
1045    /// Returns [`ClusterError`] on a malformed structure or missing required field.
1046    pub fn decode_from(r: &mut TlvReader<'_>) -> Result<Self, ClusterError> {
1047        let mut f_dst_offset_required: Option<bool> = None;
1048        loop {
1049            match r.next()? {
1050                Some(Element::ContainerEnd) => break,
1051                Some(Element::Scalar {
1052                    tag: Tag::Context(0),
1053                    value: Value::Bool(v),
1054                }) => f_dst_offset_required = Some(v),
1055                None => return Err(ClusterError::Tlv(matter_codec::Error::UnclosedContainer)),
1056                Some(Element::ContainerStart { .. }) => r.skip_container()?,
1057                Some(_) => {} // unknown/future scalar — skip
1058            }
1059        }
1060        Ok(Self {
1061            dst_offset_required: f_dst_offset_required
1062                .ok_or(ClusterError::MissingField("DstOffsetRequired"))?,
1063        })
1064    }
1065    /// Decode from a standalone anonymous TLV structure.
1066    ///
1067    /// # Errors
1068    /// Returns [`ClusterError`] if the bytes are not an anonymous structure or a field is malformed.
1069    pub fn decode(tlv: &[u8]) -> Result<Self, ClusterError> {
1070        let mut r = TlvReader::new(tlv);
1071        match r.next()? {
1072            Some(Element::ContainerStart {
1073                kind: ContainerKind::Structure,
1074                ..
1075            }) => {}
1076            _ => {
1077                return Err(ClusterError::UnexpectedType {
1078                    context: "SetTimeZoneResponse",
1079                })
1080            }
1081        }
1082        Self::decode_from(&mut r)
1083    }
1084}
1085
1086/// Encode the `SetDstOffset` command request payload.
1087#[must_use]
1088#[allow(clippy::expect_used, clippy::missing_panics_doc)] // Vec-backed TlvWriter is infallible.
1089pub fn encode_set_dst_offset(dst_offset: &Vec<DSTOffsetStruct>) -> Vec<u8> {
1090    let mut buf = Vec::new();
1091    let mut w = TlvWriter::new(&mut buf);
1092    w.start_structure(Tag::Anonymous)
1093        .expect("infallible: vec writer");
1094    w.start_array(Tag::Context(0))
1095        .expect("infallible: vec writer");
1096    for el in dst_offset.iter() {
1097        w.start_structure(Tag::Anonymous)
1098            .expect("infallible: vec writer");
1099        el.write_fields(&mut w);
1100        w.end_container().expect("infallible: vec writer");
1101    }
1102    w.end_container().expect("infallible: vec writer");
1103    w.end_container().expect("infallible: vec writer");
1104    buf
1105}
1106
1107/// Encode the `SetDefaultNtp` command request payload.
1108#[must_use]
1109#[allow(clippy::expect_used, clippy::missing_panics_doc)] // Vec-backed TlvWriter is infallible.
1110pub fn encode_set_default_ntp(default_ntp: Nullable<String>) -> Vec<u8> {
1111    let mut buf = Vec::new();
1112    let mut w = TlvWriter::new(&mut buf);
1113    w.start_structure(Tag::Anonymous)
1114        .expect("infallible: vec writer");
1115    match default_ntp {
1116        Nullable::Null => w.put_null(Tag::Context(0)).expect("infallible: vec writer"),
1117        Nullable::Value(default_ntp) => {
1118            w.put_utf8(Tag::Context(0), &default_ntp)
1119                .expect("infallible: vec writer");
1120        }
1121    }
1122    w.end_container().expect("infallible: vec writer");
1123    buf
1124}