Skip to main content

matter_clusters/gen/
basic_information.rs

1//! BasicInformation cluster (0x0028).
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 = 0x0028;
19/// Cluster revision.
20pub const CLUSTER_REVISION: u16 = 5;
21
22/// Command IDs (requests and responses).
23pub mod command_id {}
24
25/// Attribute IDs (cluster-specific).
26pub mod attribute_id {
27    /// `DataModelRevision`.
28    pub const DATA_MODEL_REVISION: u32 = 0x0000;
29    /// `VendorName`.
30    pub const VENDOR_NAME: u32 = 0x0001;
31    /// `VendorId`.
32    pub const VENDOR_ID: u32 = 0x0002;
33    /// `ProductName`.
34    pub const PRODUCT_NAME: u32 = 0x0003;
35    /// `ProductId`.
36    pub const PRODUCT_ID: u32 = 0x0004;
37    /// `NodeLabel`.
38    pub const NODE_LABEL: u32 = 0x0005;
39    /// `Location`.
40    pub const LOCATION: u32 = 0x0006;
41    /// `HardwareVersion`.
42    pub const HARDWARE_VERSION: u32 = 0x0007;
43    /// `HardwareVersionString`.
44    pub const HARDWARE_VERSION_STRING: u32 = 0x0008;
45    /// `SoftwareVersion`.
46    pub const SOFTWARE_VERSION: u32 = 0x0009;
47    /// `SoftwareVersionString`.
48    pub const SOFTWARE_VERSION_STRING: u32 = 0x000A;
49    /// `ManufacturingDate`.
50    pub const MANUFACTURING_DATE: u32 = 0x000B;
51    /// `PartNumber`.
52    pub const PART_NUMBER: u32 = 0x000C;
53    /// `ProductUrl`.
54    pub const PRODUCT_URL: u32 = 0x000D;
55    /// `ProductLabel`.
56    pub const PRODUCT_LABEL: u32 = 0x000E;
57    /// `SerialNumber`.
58    pub const SERIAL_NUMBER: u32 = 0x000F;
59    /// `LocalConfigDisabled`.
60    pub const LOCAL_CONFIG_DISABLED: u32 = 0x0010;
61    /// `Reachable`.
62    pub const REACHABLE: u32 = 0x0011;
63    /// `UniqueId`.
64    pub const UNIQUE_ID: u32 = 0x0012;
65    /// `CapabilityMinima`.
66    pub const CAPABILITY_MINIMA: u32 = 0x0013;
67    /// `ProductAppearance`.
68    pub const PRODUCT_APPEARANCE: u32 = 0x0014;
69    /// `SpecificationVersion`.
70    pub const SPECIFICATION_VERSION: u32 = 0x0015;
71    /// `MaxPathsPerInvoke`.
72    pub const MAX_PATHS_PER_INVOKE: u32 = 0x0016;
73    /// `ConfigurationVersion`.
74    pub const CONFIGURATION_VERSION: u32 = 0x0018;
75}
76
77/// `CapabilityMinimaStruct` struct.
78#[derive(Clone, Debug, PartialEq)]
79#[non_exhaustive]
80pub struct CapabilityMinimaStruct {
81    /// Field CaseSessionsPerFabric (tag 0).
82    pub case_sessions_per_fabric: u16,
83    /// Field SubscriptionsPerFabric (tag 1).
84    pub subscriptions_per_fabric: u16,
85}
86
87/// `ColorEnum` (enum8).
88#[derive(Copy, Clone, Debug, PartialEq, Eq)]
89pub enum ColorEnum {
90    /// Black = 0.
91    Black,
92    /// Navy = 1.
93    Navy,
94    /// Green = 2.
95    Green,
96    /// Teal = 3.
97    Teal,
98    /// Maroon = 4.
99    Maroon,
100    /// Purple = 5.
101    Purple,
102    /// Olive = 6.
103    Olive,
104    /// Gray = 7.
105    Gray,
106    /// Blue = 8.
107    Blue,
108    /// Lime = 9.
109    Lime,
110    /// Aqua = 10.
111    Aqua,
112    /// Red = 11.
113    Red,
114    /// Fuchsia = 12.
115    Fuchsia,
116    /// Yellow = 13.
117    Yellow,
118    /// White = 14.
119    White,
120    /// Nickel = 15.
121    Nickel,
122    /// Chrome = 16.
123    Chrome,
124    /// Brass = 17.
125    Brass,
126    /// Copper = 18.
127    Copper,
128    /// Silver = 19.
129    Silver,
130    /// Gold = 20.
131    Gold,
132    /// A value not known to this codegen revision.
133    Unknown(u8),
134}
135
136impl ColorEnum {
137    /// Decode from its raw discriminant (unknown → `Unknown`).
138    #[must_use]
139    pub fn from_raw(v: u8) -> Self {
140        match v {
141            0 => Self::Black,
142            1 => Self::Navy,
143            2 => Self::Green,
144            3 => Self::Teal,
145            4 => Self::Maroon,
146            5 => Self::Purple,
147            6 => Self::Olive,
148            7 => Self::Gray,
149            8 => Self::Blue,
150            9 => Self::Lime,
151            10 => Self::Aqua,
152            11 => Self::Red,
153            12 => Self::Fuchsia,
154            13 => Self::Yellow,
155            14 => Self::White,
156            15 => Self::Nickel,
157            16 => Self::Chrome,
158            17 => Self::Brass,
159            18 => Self::Copper,
160            19 => Self::Silver,
161            20 => Self::Gold,
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::Black => 0,
170            Self::Navy => 1,
171            Self::Green => 2,
172            Self::Teal => 3,
173            Self::Maroon => 4,
174            Self::Purple => 5,
175            Self::Olive => 6,
176            Self::Gray => 7,
177            Self::Blue => 8,
178            Self::Lime => 9,
179            Self::Aqua => 10,
180            Self::Red => 11,
181            Self::Fuchsia => 12,
182            Self::Yellow => 13,
183            Self::White => 14,
184            Self::Nickel => 15,
185            Self::Chrome => 16,
186            Self::Brass => 17,
187            Self::Copper => 18,
188            Self::Silver => 19,
189            Self::Gold => 20,
190            Self::Unknown(v) => v,
191        }
192    }
193}
194
195/// `ProductAppearanceStruct` struct.
196#[derive(Clone, Debug, PartialEq)]
197#[non_exhaustive]
198pub struct ProductAppearanceStruct {
199    /// Field Finish (tag 0).
200    pub finish: ProductFinishEnum,
201    /// Field PrimaryColor (tag 1).
202    pub primary_color: Nullable<ColorEnum>,
203}
204
205/// `ProductFinishEnum` (enum8).
206#[derive(Copy, Clone, Debug, PartialEq, Eq)]
207pub enum ProductFinishEnum {
208    /// Other = 0.
209    Other,
210    /// Matte = 1.
211    Matte,
212    /// Satin = 2.
213    Satin,
214    /// Polished = 3.
215    Polished,
216    /// Rugged = 4.
217    Rugged,
218    /// Fabric = 5.
219    Fabric,
220    /// A value not known to this codegen revision.
221    Unknown(u8),
222}
223
224impl ProductFinishEnum {
225    /// Decode from its raw discriminant (unknown → `Unknown`).
226    #[must_use]
227    pub fn from_raw(v: u8) -> Self {
228        match v {
229            0 => Self::Other,
230            1 => Self::Matte,
231            2 => Self::Satin,
232            3 => Self::Polished,
233            4 => Self::Rugged,
234            5 => Self::Fabric,
235            other => Self::Unknown(other),
236        }
237    }
238    /// The raw discriminant.
239    #[must_use]
240    pub fn to_raw(self) -> u8 {
241        match self {
242            Self::Other => 0,
243            Self::Matte => 1,
244            Self::Satin => 2,
245            Self::Polished => 3,
246            Self::Rugged => 4,
247            Self::Fabric => 5,
248            Self::Unknown(v) => v,
249        }
250    }
251}
252
253impl CapabilityMinimaStruct {
254    /// Decode the fields of an already-opened anonymous structure
255    /// (reader positioned after the struct start; consumes to its end).
256    ///
257    /// # Errors
258    /// Returns [`ClusterError`] on a malformed structure or missing required field.
259    pub fn decode_from(r: &mut TlvReader<'_>) -> Result<Self, ClusterError> {
260        let mut f_case_sessions_per_fabric: Option<u16> = None;
261        let mut f_subscriptions_per_fabric: Option<u16> = None;
262        loop {
263            match r.next()? {
264                Some(Element::ContainerEnd) => break,
265                Some(Element::Scalar {
266                    tag: Tag::Context(0),
267                    value: Value::Uint(v),
268                }) => {
269                    f_case_sessions_per_fabric = Some(
270                        u16::try_from(v)
271                            .map_err(|_| ClusterError::InvalidLength("CaseSessionsPerFabric"))?,
272                    )
273                }
274                Some(Element::Scalar {
275                    tag: Tag::Context(1),
276                    value: Value::Uint(v),
277                }) => {
278                    f_subscriptions_per_fabric = Some(
279                        u16::try_from(v)
280                            .map_err(|_| ClusterError::InvalidLength("SubscriptionsPerFabric"))?,
281                    )
282                }
283                None => return Err(ClusterError::Tlv(matter_codec::Error::UnclosedContainer)),
284                Some(Element::ContainerStart { .. }) => r.skip_container()?,
285                Some(_) => {} // unknown/future scalar — skip
286            }
287        }
288        Ok(Self {
289            case_sessions_per_fabric: f_case_sessions_per_fabric
290                .ok_or(ClusterError::MissingField("CaseSessionsPerFabric"))?,
291            subscriptions_per_fabric: f_subscriptions_per_fabric
292                .ok_or(ClusterError::MissingField("SubscriptionsPerFabric"))?,
293        })
294    }
295    /// Decode from a standalone anonymous TLV structure.
296    ///
297    /// # Errors
298    /// Returns [`ClusterError`] if the bytes are not an anonymous structure or a field is malformed.
299    pub fn decode(tlv: &[u8]) -> Result<Self, ClusterError> {
300        let mut r = TlvReader::new(tlv);
301        match r.next()? {
302            Some(Element::ContainerStart {
303                kind: ContainerKind::Structure,
304                ..
305            }) => {}
306            _ => {
307                return Err(ClusterError::UnexpectedType {
308                    context: "CapabilityMinimaStruct",
309                })
310            }
311        }
312        Self::decode_from(&mut r)
313    }
314    /// Write this struct's fields into an already-open container.
315    #[allow(clippy::expect_used)] // Vec-backed TlvWriter is infallible.
316    pub fn write_fields(&self, w: &mut TlvWriter<'_>) {
317        w.put_uint(Tag::Context(0), u64::from(self.case_sessions_per_fabric))
318            .expect("infallible: vec writer");
319        w.put_uint(Tag::Context(1), u64::from(self.subscriptions_per_fabric))
320            .expect("infallible: vec writer");
321    }
322    /// Encode as a standalone anonymous TLV structure.
323    #[must_use]
324    #[allow(clippy::expect_used)] // Vec-backed TlvWriter is infallible.
325    pub fn encode(&self) -> Vec<u8> {
326        let mut buf = Vec::new();
327        let mut w = TlvWriter::new(&mut buf);
328        w.start_structure(Tag::Anonymous)
329            .expect("infallible: vec writer");
330        self.write_fields(&mut w);
331        w.end_container().expect("infallible: vec writer");
332        buf
333    }
334}
335
336impl ProductAppearanceStruct {
337    /// Decode the fields of an already-opened anonymous structure
338    /// (reader positioned after the struct start; consumes to its end).
339    ///
340    /// # Errors
341    /// Returns [`ClusterError`] on a malformed structure or missing required field.
342    pub fn decode_from(r: &mut TlvReader<'_>) -> Result<Self, ClusterError> {
343        let mut f_finish: Option<ProductFinishEnum> = None;
344        let mut f_primary_color: Option<Nullable<ColorEnum>> = None;
345        loop {
346            match r.next()? {
347                Some(Element::ContainerEnd) => break,
348                Some(Element::Scalar {
349                    tag: Tag::Context(0),
350                    value: Value::Uint(v),
351                }) => {
352                    f_finish = Some(ProductFinishEnum::from_raw(
353                        u8::try_from(v).map_err(|_| ClusterError::InvalidLength("Finish"))?,
354                    ))
355                }
356                Some(Element::Scalar {
357                    tag: Tag::Context(1),
358                    value: Value::Null,
359                }) => f_primary_color = Some(Nullable::Null),
360                Some(Element::Scalar {
361                    tag: Tag::Context(1),
362                    value: Value::Uint(v),
363                }) => {
364                    f_primary_color = Some(Nullable::Value(ColorEnum::from_raw(
365                        u8::try_from(v).map_err(|_| ClusterError::InvalidLength("PrimaryColor"))?,
366                    )))
367                }
368                None => return Err(ClusterError::Tlv(matter_codec::Error::UnclosedContainer)),
369                Some(Element::ContainerStart { .. }) => r.skip_container()?,
370                Some(_) => {} // unknown/future scalar — skip
371            }
372        }
373        Ok(Self {
374            finish: f_finish.ok_or(ClusterError::MissingField("Finish"))?,
375            primary_color: f_primary_color.ok_or(ClusterError::MissingField("PrimaryColor"))?,
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: "ProductAppearanceStruct",
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_uint(Tag::Context(0), u64::from(self.finish.to_raw()))
401            .expect("infallible: vec writer");
402        match &self.primary_color {
403            Nullable::Null => w.put_null(Tag::Context(1)).expect("infallible: vec writer"),
404            Nullable::Value(primary_color) => {
405                w.put_uint(Tag::Context(1), u64::from((*primary_color).to_raw()))
406                    .expect("infallible: vec writer");
407            }
408        }
409    }
410    /// Encode as a standalone anonymous TLV structure.
411    #[must_use]
412    #[allow(clippy::expect_used)] // Vec-backed TlvWriter is infallible.
413    pub fn encode(&self) -> Vec<u8> {
414        let mut buf = Vec::new();
415        let mut w = TlvWriter::new(&mut buf);
416        w.start_structure(Tag::Anonymous)
417            .expect("infallible: vec writer");
418        self.write_fields(&mut w);
419        w.end_container().expect("infallible: vec writer");
420        buf
421    }
422}
423
424/// Decode the `DataModelRevision` attribute value.
425///
426/// # Errors
427/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
428pub fn decode_data_model_revision(tlv: &[u8]) -> Result<u16, ClusterError> {
429    let mut r = TlvReader::new(tlv);
430    match r.next()? {
431        Some(Element::Scalar {
432            value: Value::Uint(v),
433            ..
434        }) => Ok(u16::try_from(v).map_err(|_| ClusterError::InvalidLength("DataModelRevision"))?),
435        _ => Err(ClusterError::UnexpectedType {
436            context: "DataModelRevision",
437        }),
438    }
439}
440
441/// Decode the `VendorName` attribute value.
442///
443/// # Errors
444/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
445pub fn decode_vendor_name(tlv: &[u8]) -> Result<String, ClusterError> {
446    let mut r = TlvReader::new(tlv);
447    match r.next()? {
448        Some(Element::Scalar {
449            value: Value::Utf8(v),
450            ..
451        }) => Ok(v),
452        _ => Err(ClusterError::UnexpectedType {
453            context: "VendorName",
454        }),
455    }
456}
457
458/// Decode the `VendorId` attribute value.
459///
460/// # Errors
461/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
462pub fn decode_vendor_id(tlv: &[u8]) -> Result<u16, ClusterError> {
463    let mut r = TlvReader::new(tlv);
464    match r.next()? {
465        Some(Element::Scalar {
466            value: Value::Uint(v),
467            ..
468        }) => Ok(u16::try_from(v).map_err(|_| ClusterError::InvalidLength("VendorId"))?),
469        _ => Err(ClusterError::UnexpectedType {
470            context: "VendorId",
471        }),
472    }
473}
474
475/// Decode the `ProductName` attribute value.
476///
477/// # Errors
478/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
479pub fn decode_product_name(tlv: &[u8]) -> Result<String, ClusterError> {
480    let mut r = TlvReader::new(tlv);
481    match r.next()? {
482        Some(Element::Scalar {
483            value: Value::Utf8(v),
484            ..
485        }) => Ok(v),
486        _ => Err(ClusterError::UnexpectedType {
487            context: "ProductName",
488        }),
489    }
490}
491
492/// Decode the `ProductId` attribute value.
493///
494/// # Errors
495/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
496pub fn decode_product_id(tlv: &[u8]) -> Result<u16, ClusterError> {
497    let mut r = TlvReader::new(tlv);
498    match r.next()? {
499        Some(Element::Scalar {
500            value: Value::Uint(v),
501            ..
502        }) => Ok(u16::try_from(v).map_err(|_| ClusterError::InvalidLength("ProductId"))?),
503        _ => Err(ClusterError::UnexpectedType {
504            context: "ProductId",
505        }),
506    }
507}
508
509/// Decode the `NodeLabel` attribute value.
510///
511/// # Errors
512/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
513pub fn decode_node_label(tlv: &[u8]) -> Result<String, ClusterError> {
514    let mut r = TlvReader::new(tlv);
515    match r.next()? {
516        Some(Element::Scalar {
517            value: Value::Utf8(v),
518            ..
519        }) => Ok(v),
520        _ => Err(ClusterError::UnexpectedType {
521            context: "NodeLabel",
522        }),
523    }
524}
525
526/// Encode the `NodeLabel` attribute value as a standalone TLV element.
527#[must_use]
528#[allow(clippy::expect_used, clippy::missing_panics_doc)] // Vec-backed TlvWriter is infallible.
529pub fn encode_node_label(value: &String) -> Vec<u8> {
530    let mut buf = Vec::new();
531    let mut w = TlvWriter::new(&mut buf);
532    w.put_utf8(Tag::Anonymous, &value)
533        .expect("infallible: vec writer");
534    buf
535}
536
537/// Decode the `Location` attribute value.
538///
539/// # Errors
540/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
541pub fn decode_location(tlv: &[u8]) -> Result<String, ClusterError> {
542    let mut r = TlvReader::new(tlv);
543    match r.next()? {
544        Some(Element::Scalar {
545            value: Value::Utf8(v),
546            ..
547        }) => Ok(v),
548        _ => Err(ClusterError::UnexpectedType {
549            context: "Location",
550        }),
551    }
552}
553
554/// Encode the `Location` attribute value as a standalone TLV element.
555#[must_use]
556#[allow(clippy::expect_used, clippy::missing_panics_doc)] // Vec-backed TlvWriter is infallible.
557pub fn encode_location(value: &String) -> Vec<u8> {
558    let mut buf = Vec::new();
559    let mut w = TlvWriter::new(&mut buf);
560    w.put_utf8(Tag::Anonymous, &value)
561        .expect("infallible: vec writer");
562    buf
563}
564
565/// Decode the `HardwareVersion` attribute value.
566///
567/// # Errors
568/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
569pub fn decode_hardware_version(tlv: &[u8]) -> Result<u16, ClusterError> {
570    let mut r = TlvReader::new(tlv);
571    match r.next()? {
572        Some(Element::Scalar {
573            value: Value::Uint(v),
574            ..
575        }) => Ok(u16::try_from(v).map_err(|_| ClusterError::InvalidLength("HardwareVersion"))?),
576        _ => Err(ClusterError::UnexpectedType {
577            context: "HardwareVersion",
578        }),
579    }
580}
581
582/// Decode the `HardwareVersionString` attribute value.
583///
584/// # Errors
585/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
586pub fn decode_hardware_version_string(tlv: &[u8]) -> Result<String, ClusterError> {
587    let mut r = TlvReader::new(tlv);
588    match r.next()? {
589        Some(Element::Scalar {
590            value: Value::Utf8(v),
591            ..
592        }) => Ok(v),
593        _ => Err(ClusterError::UnexpectedType {
594            context: "HardwareVersionString",
595        }),
596    }
597}
598
599/// Decode the `SoftwareVersion` attribute value.
600///
601/// # Errors
602/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
603pub fn decode_software_version(tlv: &[u8]) -> Result<u32, ClusterError> {
604    let mut r = TlvReader::new(tlv);
605    match r.next()? {
606        Some(Element::Scalar {
607            value: Value::Uint(v),
608            ..
609        }) => Ok(u32::try_from(v).map_err(|_| ClusterError::InvalidLength("SoftwareVersion"))?),
610        _ => Err(ClusterError::UnexpectedType {
611            context: "SoftwareVersion",
612        }),
613    }
614}
615
616/// Decode the `SoftwareVersionString` attribute value.
617///
618/// # Errors
619/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
620pub fn decode_software_version_string(tlv: &[u8]) -> Result<String, ClusterError> {
621    let mut r = TlvReader::new(tlv);
622    match r.next()? {
623        Some(Element::Scalar {
624            value: Value::Utf8(v),
625            ..
626        }) => Ok(v),
627        _ => Err(ClusterError::UnexpectedType {
628            context: "SoftwareVersionString",
629        }),
630    }
631}
632
633/// Decode the `ManufacturingDate` attribute value.
634///
635/// # Errors
636/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
637pub fn decode_manufacturing_date(tlv: &[u8]) -> Result<String, ClusterError> {
638    let mut r = TlvReader::new(tlv);
639    match r.next()? {
640        Some(Element::Scalar {
641            value: Value::Utf8(v),
642            ..
643        }) => Ok(v),
644        _ => Err(ClusterError::UnexpectedType {
645            context: "ManufacturingDate",
646        }),
647    }
648}
649
650/// Decode the `PartNumber` attribute value.
651///
652/// # Errors
653/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
654pub fn decode_part_number(tlv: &[u8]) -> Result<String, ClusterError> {
655    let mut r = TlvReader::new(tlv);
656    match r.next()? {
657        Some(Element::Scalar {
658            value: Value::Utf8(v),
659            ..
660        }) => Ok(v),
661        _ => Err(ClusterError::UnexpectedType {
662            context: "PartNumber",
663        }),
664    }
665}
666
667/// Decode the `ProductUrl` attribute value.
668///
669/// # Errors
670/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
671pub fn decode_product_url(tlv: &[u8]) -> Result<String, ClusterError> {
672    let mut r = TlvReader::new(tlv);
673    match r.next()? {
674        Some(Element::Scalar {
675            value: Value::Utf8(v),
676            ..
677        }) => Ok(v),
678        _ => Err(ClusterError::UnexpectedType {
679            context: "ProductUrl",
680        }),
681    }
682}
683
684/// Decode the `ProductLabel` attribute value.
685///
686/// # Errors
687/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
688pub fn decode_product_label(tlv: &[u8]) -> Result<String, ClusterError> {
689    let mut r = TlvReader::new(tlv);
690    match r.next()? {
691        Some(Element::Scalar {
692            value: Value::Utf8(v),
693            ..
694        }) => Ok(v),
695        _ => Err(ClusterError::UnexpectedType {
696            context: "ProductLabel",
697        }),
698    }
699}
700
701/// Decode the `SerialNumber` attribute value.
702///
703/// # Errors
704/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
705pub fn decode_serial_number(tlv: &[u8]) -> Result<String, ClusterError> {
706    let mut r = TlvReader::new(tlv);
707    match r.next()? {
708        Some(Element::Scalar {
709            value: Value::Utf8(v),
710            ..
711        }) => Ok(v),
712        _ => Err(ClusterError::UnexpectedType {
713            context: "SerialNumber",
714        }),
715    }
716}
717
718/// Decode the `LocalConfigDisabled` attribute value.
719///
720/// # Errors
721/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
722pub fn decode_local_config_disabled(tlv: &[u8]) -> Result<bool, ClusterError> {
723    let mut r = TlvReader::new(tlv);
724    match r.next()? {
725        Some(Element::Scalar {
726            value: Value::Bool(v),
727            ..
728        }) => Ok(v),
729        _ => Err(ClusterError::UnexpectedType {
730            context: "LocalConfigDisabled",
731        }),
732    }
733}
734
735/// Encode the `LocalConfigDisabled` attribute value as a standalone TLV element.
736#[must_use]
737#[allow(clippy::expect_used, clippy::missing_panics_doc)] // Vec-backed TlvWriter is infallible.
738pub fn encode_local_config_disabled(value: bool) -> Vec<u8> {
739    let mut buf = Vec::new();
740    let mut w = TlvWriter::new(&mut buf);
741    w.put_bool(Tag::Anonymous, value)
742        .expect("infallible: vec writer");
743    buf
744}
745
746/// Decode the `Reachable` attribute value.
747///
748/// # Errors
749/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
750pub fn decode_reachable(tlv: &[u8]) -> Result<bool, ClusterError> {
751    let mut r = TlvReader::new(tlv);
752    match r.next()? {
753        Some(Element::Scalar {
754            value: Value::Bool(v),
755            ..
756        }) => Ok(v),
757        _ => Err(ClusterError::UnexpectedType {
758            context: "Reachable",
759        }),
760    }
761}
762
763/// Decode the `UniqueId` attribute value.
764///
765/// # Errors
766/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
767pub fn decode_unique_id(tlv: &[u8]) -> Result<String, ClusterError> {
768    let mut r = TlvReader::new(tlv);
769    match r.next()? {
770        Some(Element::Scalar {
771            value: Value::Utf8(v),
772            ..
773        }) => Ok(v),
774        _ => Err(ClusterError::UnexpectedType {
775            context: "UniqueId",
776        }),
777    }
778}
779
780/// Decode the `CapabilityMinima` attribute value.
781///
782/// # Errors
783/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
784pub fn decode_capability_minima(tlv: &[u8]) -> Result<CapabilityMinimaStruct, ClusterError> {
785    CapabilityMinimaStruct::decode(tlv)
786}
787
788/// Decode the `ProductAppearance` attribute value.
789///
790/// # Errors
791/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
792pub fn decode_product_appearance(tlv: &[u8]) -> Result<ProductAppearanceStruct, ClusterError> {
793    ProductAppearanceStruct::decode(tlv)
794}
795
796/// Decode the `SpecificationVersion` attribute value.
797///
798/// # Errors
799/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
800pub fn decode_specification_version(tlv: &[u8]) -> Result<u32, ClusterError> {
801    let mut r = TlvReader::new(tlv);
802    match r.next()? {
803        Some(Element::Scalar {
804            value: Value::Uint(v),
805            ..
806        }) => Ok(u32::try_from(v).map_err(|_| ClusterError::InvalidLength("SpecificationVersion"))?),
807        _ => Err(ClusterError::UnexpectedType {
808            context: "SpecificationVersion",
809        }),
810    }
811}
812
813/// Decode the `MaxPathsPerInvoke` attribute value.
814///
815/// # Errors
816/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
817pub fn decode_max_paths_per_invoke(tlv: &[u8]) -> Result<u16, ClusterError> {
818    let mut r = TlvReader::new(tlv);
819    match r.next()? {
820        Some(Element::Scalar {
821            value: Value::Uint(v),
822            ..
823        }) => Ok(u16::try_from(v).map_err(|_| ClusterError::InvalidLength("MaxPathsPerInvoke"))?),
824        _ => Err(ClusterError::UnexpectedType {
825            context: "MaxPathsPerInvoke",
826        }),
827    }
828}
829
830/// Decode the `ConfigurationVersion` attribute value.
831///
832/// # Errors
833/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
834pub fn decode_configuration_version(tlv: &[u8]) -> Result<u32, ClusterError> {
835    let mut r = TlvReader::new(tlv);
836    match r.next()? {
837        Some(Element::Scalar {
838            value: Value::Uint(v),
839            ..
840        }) => Ok(u32::try_from(v).map_err(|_| ClusterError::InvalidLength("ConfigurationVersion"))?),
841        _ => Err(ClusterError::UnexpectedType {
842            context: "ConfigurationVersion",
843        }),
844    }
845}