matc/clusters/codec/
acl_cluster.rs

1//! Matter TLV encoders and decoders for Access Control Cluster
2//! Cluster ID: 0x001F
3//!
4//! This file is automatically generated from ACL-Cluster.xml
5
6#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13// Import serialization helpers for octet strings
14use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
15
16// Enum definitions
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
19#[repr(u8)]
20pub enum AccessControlEntryAuthMode {
21    /// Passcode authenticated session
22    Pase = 1,
23    /// Certificate authenticated session
24    Case = 2,
25    /// Group authenticated session
26    Group = 3,
27}
28
29impl AccessControlEntryAuthMode {
30    /// Convert from u8 value
31    pub fn from_u8(value: u8) -> Option<Self> {
32        match value {
33            1 => Some(AccessControlEntryAuthMode::Pase),
34            2 => Some(AccessControlEntryAuthMode::Case),
35            3 => Some(AccessControlEntryAuthMode::Group),
36            _ => None,
37        }
38    }
39
40    /// Convert to u8 value
41    pub fn to_u8(self) -> u8 {
42        self as u8
43    }
44}
45
46impl From<AccessControlEntryAuthMode> for u8 {
47    fn from(val: AccessControlEntryAuthMode) -> Self {
48        val as u8
49    }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
53#[repr(u8)]
54pub enum AccessControlEntryPrivilege {
55    /// Can read and observe all (except Access Control Cluster)
56    View = 1,
57    Proxyview = 2,
58    /// View privileges, and can perform the primary function of this Node (except Access Control Cluster)
59    Operate = 3,
60    /// Operate privileges, and can modify persistent configuration of this Node (except Access Control Cluster)
61    Manage = 4,
62    /// Manage privileges, and can observe and modify the Access Control Cluster
63    Administer = 5,
64}
65
66impl AccessControlEntryPrivilege {
67    /// Convert from u8 value
68    pub fn from_u8(value: u8) -> Option<Self> {
69        match value {
70            1 => Some(AccessControlEntryPrivilege::View),
71            2 => Some(AccessControlEntryPrivilege::Proxyview),
72            3 => Some(AccessControlEntryPrivilege::Operate),
73            4 => Some(AccessControlEntryPrivilege::Manage),
74            5 => Some(AccessControlEntryPrivilege::Administer),
75            _ => None,
76        }
77    }
78
79    /// Convert to u8 value
80    pub fn to_u8(self) -> u8 {
81        self as u8
82    }
83}
84
85impl From<AccessControlEntryPrivilege> for u8 {
86    fn from(val: AccessControlEntryPrivilege) -> Self {
87        val as u8
88    }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
92#[repr(u8)]
93pub enum AccessRestrictionType {
94    /// Clients on this fabric are currently forbidden from reading and writing an attribute
95    Attributeaccessforbidden = 0,
96    /// Clients on this fabric are currently forbidden from writing an attribute
97    Attributewriteforbidden = 1,
98    /// Clients on this fabric are currently forbidden from invoking a command
99    Commandforbidden = 2,
100    /// Clients on this fabric are currently forbidden from reading an event
101    Eventforbidden = 3,
102}
103
104impl AccessRestrictionType {
105    /// Convert from u8 value
106    pub fn from_u8(value: u8) -> Option<Self> {
107        match value {
108            0 => Some(AccessRestrictionType::Attributeaccessforbidden),
109            1 => Some(AccessRestrictionType::Attributewriteforbidden),
110            2 => Some(AccessRestrictionType::Commandforbidden),
111            3 => Some(AccessRestrictionType::Eventforbidden),
112            _ => None,
113        }
114    }
115
116    /// Convert to u8 value
117    pub fn to_u8(self) -> u8 {
118        self as u8
119    }
120}
121
122impl From<AccessRestrictionType> for u8 {
123    fn from(val: AccessRestrictionType) -> Self {
124        val as u8
125    }
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
129#[repr(u8)]
130pub enum ChangeType {
131    /// Entry or extension was changed
132    Changed = 0,
133    /// Entry or extension was added
134    Added = 1,
135    /// Entry or extension was removed
136    Removed = 2,
137}
138
139impl ChangeType {
140    /// Convert from u8 value
141    pub fn from_u8(value: u8) -> Option<Self> {
142        match value {
143            0 => Some(ChangeType::Changed),
144            1 => Some(ChangeType::Added),
145            2 => Some(ChangeType::Removed),
146            _ => None,
147        }
148    }
149
150    /// Convert to u8 value
151    pub fn to_u8(self) -> u8 {
152        self as u8
153    }
154}
155
156impl From<ChangeType> for u8 {
157    fn from(val: ChangeType) -> Self {
158        val as u8
159    }
160}
161
162// Struct definitions
163
164#[derive(Debug, serde::Serialize)]
165pub struct AccessControlEntry {
166    pub privilege: Option<AccessControlEntryPrivilege>,
167    pub auth_mode: Option<AccessControlEntryAuthMode>,
168    pub subjects: Option<Vec<u64>>,
169    pub targets: Option<Vec<AccessControlTarget>>,
170}
171
172#[derive(Debug, serde::Serialize)]
173pub struct AccessControlExtension {
174    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
175    pub data: Option<Vec<u8>>,
176}
177
178#[derive(Debug, serde::Serialize)]
179pub struct AccessControlTarget {
180    pub cluster: Option<u32>,
181    pub endpoint: Option<u16>,
182    pub device_type: Option<u32>,
183}
184
185#[derive(Debug, serde::Serialize)]
186pub struct AccessRestrictionEntry {
187    pub endpoint: Option<u16>,
188    pub cluster: Option<u32>,
189    pub restrictions: Option<Vec<AccessRestriction>>,
190}
191
192#[derive(Debug, serde::Serialize)]
193pub struct AccessRestriction {
194    pub type_: Option<AccessRestrictionType>,
195    pub id: Option<u32>,
196}
197
198#[derive(Debug, serde::Serialize)]
199pub struct CommissioningAccessRestrictionEntry {
200    pub endpoint: Option<u16>,
201    pub cluster: Option<u32>,
202    pub restrictions: Option<Vec<AccessRestriction>>,
203}
204
205// Command encoders
206
207/// Encode ReviewFabricRestrictions command (0x00)
208pub fn encode_review_fabric_restrictions(arl: Vec<CommissioningAccessRestrictionEntry>) -> anyhow::Result<Vec<u8>> {
209    let tlv = tlv::TlvItemEnc {
210        tag: 0,
211        value: tlv::TlvItemValueEnc::StructInvisible(vec![
212        (0, tlv::TlvItemValueEnc::Array(arl.into_iter().map(|v| {
213                    let mut fields = Vec::new();
214                    if let Some(x) = v.endpoint { fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
215                    if let Some(x) = v.cluster { fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
216                    if let Some(listv) = v.restrictions {
217                        let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
218                            let mut nested_fields = Vec::new();
219                                if let Some(x) = inner.type_ { nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
220                                if let Some(x) = inner.id { nested_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
221                            (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
222                        }).collect();
223                        fields.push((2, tlv::TlvItemValueEnc::Array(inner_vec)).into());
224                    }
225                    (0, tlv::TlvItemValueEnc::StructAnon(fields)).into()
226                }).collect())).into(),
227        ]),
228    };
229    Ok(tlv.encode()?)
230}
231
232// Attribute decoders
233
234/// Decode ACL attribute (0x0000)
235pub fn decode_acl(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AccessControlEntry>> {
236    let mut res = Vec::new();
237    if let tlv::TlvItemValue::List(v) = inp {
238        for item in v {
239            res.push(AccessControlEntry {
240                privilege: item.get_int(&[1]).and_then(|v| AccessControlEntryPrivilege::from_u8(v as u8)),
241                auth_mode: item.get_int(&[2]).and_then(|v| AccessControlEntryAuthMode::from_u8(v as u8)),
242                subjects: {
243                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
244                        let items: Vec<u64> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v) } else { None } }).collect();
245                        Some(items)
246                    } else {
247                        None
248                    }
249                },
250                targets: {
251                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[4]) {
252                        let mut items = Vec::new();
253                        for list_item in l {
254                            items.push(AccessControlTarget {
255                cluster: list_item.get_int(&[0]).map(|v| v as u32),
256                endpoint: list_item.get_int(&[1]).map(|v| v as u16),
257                device_type: list_item.get_int(&[2]).map(|v| v as u32),
258                            });
259                        }
260                        Some(items)
261                    } else {
262                        None
263                    }
264                },
265            });
266        }
267    }
268    Ok(res)
269}
270
271/// Decode Extension attribute (0x0001)
272pub fn decode_extension(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AccessControlExtension>> {
273    let mut res = Vec::new();
274    if let tlv::TlvItemValue::List(v) = inp {
275        for item in v {
276            res.push(AccessControlExtension {
277                data: item.get_octet_string_owned(&[1]),
278            });
279        }
280    }
281    Ok(res)
282}
283
284/// Decode SubjectsPerAccessControlEntry attribute (0x0002)
285pub fn decode_subjects_per_access_control_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
286    if let tlv::TlvItemValue::Int(v) = inp {
287        Ok(*v as u16)
288    } else {
289        Err(anyhow::anyhow!("Expected UInt16"))
290    }
291}
292
293/// Decode TargetsPerAccessControlEntry attribute (0x0003)
294pub fn decode_targets_per_access_control_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
295    if let tlv::TlvItemValue::Int(v) = inp {
296        Ok(*v as u16)
297    } else {
298        Err(anyhow::anyhow!("Expected UInt16"))
299    }
300}
301
302/// Decode AccessControlEntriesPerFabric attribute (0x0004)
303pub fn decode_access_control_entries_per_fabric(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
304    if let tlv::TlvItemValue::Int(v) = inp {
305        Ok(*v as u16)
306    } else {
307        Err(anyhow::anyhow!("Expected UInt16"))
308    }
309}
310
311/// Decode CommissioningARL attribute (0x0005)
312pub fn decode_commissioning_arl(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<CommissioningAccessRestrictionEntry>> {
313    let mut res = Vec::new();
314    if let tlv::TlvItemValue::List(v) = inp {
315        for item in v {
316            res.push(CommissioningAccessRestrictionEntry {
317                endpoint: item.get_int(&[0]).map(|v| v as u16),
318                cluster: item.get_int(&[1]).map(|v| v as u32),
319                restrictions: {
320                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
321                        let mut items = Vec::new();
322                        for list_item in l {
323                            items.push(AccessRestriction {
324                type_: list_item.get_int(&[0]).and_then(|v| AccessRestrictionType::from_u8(v as u8)),
325                id: list_item.get_int(&[1]).map(|v| v as u32),
326                            });
327                        }
328                        Some(items)
329                    } else {
330                        None
331                    }
332                },
333            });
334        }
335    }
336    Ok(res)
337}
338
339/// Decode ARL attribute (0x0006)
340pub fn decode_arl(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AccessRestrictionEntry>> {
341    let mut res = Vec::new();
342    if let tlv::TlvItemValue::List(v) = inp {
343        for item in v {
344            res.push(AccessRestrictionEntry {
345                endpoint: item.get_int(&[0]).map(|v| v as u16),
346                cluster: item.get_int(&[1]).map(|v| v as u32),
347                restrictions: {
348                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
349                        let mut items = Vec::new();
350                        for list_item in l {
351                            items.push(AccessRestriction {
352                type_: list_item.get_int(&[0]).and_then(|v| AccessRestrictionType::from_u8(v as u8)),
353                id: list_item.get_int(&[1]).map(|v| v as u32),
354                            });
355                        }
356                        Some(items)
357                    } else {
358                        None
359                    }
360                },
361            });
362        }
363    }
364    Ok(res)
365}
366
367
368// JSON dispatcher function
369
370/// Decode attribute value and return as JSON string
371///
372/// # Parameters
373/// * `cluster_id` - The cluster identifier
374/// * `attribute_id` - The attribute identifier
375/// * `tlv_value` - The TLV value to decode
376///
377/// # Returns
378/// JSON string representation of the decoded value or error
379pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
380    // Verify this is the correct cluster
381    if cluster_id != 0x001F {
382        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x001F, got {}\"}}", cluster_id);
383    }
384
385    match attribute_id {
386        0x0000 => {
387            match decode_acl(tlv_value) {
388                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
389                Err(e) => format!("{{\"error\": \"{}\"}}", e),
390            }
391        }
392        0x0001 => {
393            match decode_extension(tlv_value) {
394                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
395                Err(e) => format!("{{\"error\": \"{}\"}}", e),
396            }
397        }
398        0x0002 => {
399            match decode_subjects_per_access_control_entry(tlv_value) {
400                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
401                Err(e) => format!("{{\"error\": \"{}\"}}", e),
402            }
403        }
404        0x0003 => {
405            match decode_targets_per_access_control_entry(tlv_value) {
406                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
407                Err(e) => format!("{{\"error\": \"{}\"}}", e),
408            }
409        }
410        0x0004 => {
411            match decode_access_control_entries_per_fabric(tlv_value) {
412                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
413                Err(e) => format!("{{\"error\": \"{}\"}}", e),
414            }
415        }
416        0x0005 => {
417            match decode_commissioning_arl(tlv_value) {
418                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
419                Err(e) => format!("{{\"error\": \"{}\"}}", e),
420            }
421        }
422        0x0006 => {
423            match decode_arl(tlv_value) {
424                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
425                Err(e) => format!("{{\"error\": \"{}\"}}", e),
426            }
427        }
428        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
429    }
430}
431
432/// Get list of all attributes supported by this cluster
433///
434/// # Returns
435/// Vector of tuples containing (attribute_id, attribute_name)
436pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
437    vec![
438        (0x0000, "ACL"),
439        (0x0001, "Extension"),
440        (0x0002, "SubjectsPerAccessControlEntry"),
441        (0x0003, "TargetsPerAccessControlEntry"),
442        (0x0004, "AccessControlEntriesPerFabric"),
443        (0x0005, "CommissioningARL"),
444        (0x0006, "ARL"),
445    ]
446}
447
448// Command listing
449
450pub fn get_command_list() -> Vec<(u32, &'static str)> {
451    vec![
452        (0x00, "ReviewFabricRestrictions"),
453    ]
454}
455
456pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
457    match cmd_id {
458        0x00 => Some("ReviewFabricRestrictions"),
459        _ => None,
460    }
461}
462
463pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
464    match cmd_id {
465        0x00 => Some(vec![
466            crate::clusters::codec::CommandField { tag: 0, name: "arl", kind: crate::clusters::codec::FieldKind::List { entry_type: "CommissioningAccessRestrictionEntryStruct" }, optional: false, nullable: false },
467        ]),
468        _ => None,
469    }
470}
471
472pub fn encode_command_json(cmd_id: u32, _args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
473    match cmd_id {
474        0x00 => Err(anyhow::anyhow!("command \"ReviewFabricRestrictions\" has complex args: use raw mode")),
475        _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
476    }
477}
478
479#[derive(Debug, serde::Serialize)]
480pub struct ReviewFabricRestrictionsResponse {
481    pub token: Option<u64>,
482}
483
484// Command response decoders
485
486/// Decode ReviewFabricRestrictionsResponse command response (01)
487pub fn decode_review_fabric_restrictions_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ReviewFabricRestrictionsResponse> {
488    if let tlv::TlvItemValue::List(_fields) = inp {
489        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
490        Ok(ReviewFabricRestrictionsResponse {
491                token: item.get_int(&[0]),
492        })
493    } else {
494        Err(anyhow::anyhow!("Expected struct fields"))
495    }
496}
497
498// Typed facade (invokes + reads)
499
500/// Invoke `ReviewFabricRestrictions` command on cluster `Access Control`.
501pub async fn review_fabric_restrictions(conn: &crate::controller::Connection, endpoint: u16, arl: Vec<CommissioningAccessRestrictionEntry>) -> anyhow::Result<ReviewFabricRestrictionsResponse> {
502    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_CMD_ID_REVIEWFABRICRESTRICTIONS, &encode_review_fabric_restrictions(arl)?).await?;
503    decode_review_fabric_restrictions_response(&tlv)
504}
505
506/// Read `ACL` attribute from cluster `Access Control`.
507pub async fn read_acl(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<AccessControlEntry>> {
508    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_ATTR_ID_ACL).await?;
509    decode_acl(&tlv)
510}
511
512/// Read `Extension` attribute from cluster `Access Control`.
513pub async fn read_extension(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<AccessControlExtension>> {
514    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_ATTR_ID_EXTENSION).await?;
515    decode_extension(&tlv)
516}
517
518/// Read `SubjectsPerAccessControlEntry` attribute from cluster `Access Control`.
519pub async fn read_subjects_per_access_control_entry(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
520    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_ATTR_ID_SUBJECTSPERACCESSCONTROLENTRY).await?;
521    decode_subjects_per_access_control_entry(&tlv)
522}
523
524/// Read `TargetsPerAccessControlEntry` attribute from cluster `Access Control`.
525pub async fn read_targets_per_access_control_entry(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
526    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_ATTR_ID_TARGETSPERACCESSCONTROLENTRY).await?;
527    decode_targets_per_access_control_entry(&tlv)
528}
529
530/// Read `AccessControlEntriesPerFabric` attribute from cluster `Access Control`.
531pub async fn read_access_control_entries_per_fabric(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
532    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_ATTR_ID_ACCESSCONTROLENTRIESPERFABRIC).await?;
533    decode_access_control_entries_per_fabric(&tlv)
534}
535
536/// Read `CommissioningARL` attribute from cluster `Access Control`.
537pub async fn read_commissioning_arl(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<CommissioningAccessRestrictionEntry>> {
538    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_ATTR_ID_COMMISSIONINGARL).await?;
539    decode_commissioning_arl(&tlv)
540}
541
542/// Read `ARL` attribute from cluster `Access Control`.
543pub async fn read_arl(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<AccessRestrictionEntry>> {
544    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_ATTR_ID_ARL).await?;
545    decode_arl(&tlv)
546}
547
548#[derive(Debug, serde::Serialize)]
549pub struct AccessControlEntryChangedEvent {
550    pub admin_node_id: Option<u64>,
551    pub admin_passcode_id: Option<u16>,
552    pub change_type: Option<ChangeType>,
553    pub latest_value: Option<AccessControlEntry>,
554}
555
556#[derive(Debug, serde::Serialize)]
557pub struct AccessControlExtensionChangedEvent {
558    pub admin_node_id: Option<u64>,
559    pub admin_passcode_id: Option<u16>,
560    pub change_type: Option<ChangeType>,
561    pub latest_value: Option<AccessControlExtension>,
562}
563
564#[derive(Debug, serde::Serialize)]
565pub struct FabricRestrictionReviewUpdateEvent {
566    pub token: Option<u64>,
567    pub instruction: Option<String>,
568    pub arl_request_flow_url: Option<String>,
569}
570
571// Event decoders
572
573/// Decode AccessControlEntryChanged event (0x00, priority: info)
574pub fn decode_access_control_entry_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<AccessControlEntryChangedEvent> {
575    if let tlv::TlvItemValue::List(_fields) = inp {
576        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
577        Ok(AccessControlEntryChangedEvent {
578                                admin_node_id: item.get_int(&[1]),
579                                admin_passcode_id: item.get_int(&[2]).map(|v| v as u16),
580                                change_type: item.get_int(&[3]).and_then(|v| ChangeType::from_u8(v as u8)),
581                                latest_value: {
582                    if let Some(nested_tlv) = item.get(&[4]) {
583                        if let tlv::TlvItemValue::List(_) = nested_tlv {
584                            let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
585                            Some(AccessControlEntry {
586                privilege: nested_item.get_int(&[1]).and_then(|v| AccessControlEntryPrivilege::from_u8(v as u8)),
587                auth_mode: nested_item.get_int(&[2]).and_then(|v| AccessControlEntryAuthMode::from_u8(v as u8)),
588                subjects: {
589                    if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[3]) {
590                        let items: Vec<u64> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v) } else { None } }).collect();
591                        Some(items)
592                    } else {
593                        None
594                    }
595                },
596                targets: {
597                    if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[4]) {
598                        let mut items = Vec::new();
599                        for list_item in l {
600                            items.push(AccessControlTarget {
601                cluster: list_item.get_int(&[0]).map(|v| v as u32),
602                endpoint: list_item.get_int(&[1]).map(|v| v as u16),
603                device_type: list_item.get_int(&[2]).map(|v| v as u32),
604                            });
605                        }
606                        Some(items)
607                    } else {
608                        None
609                    }
610                },
611                            })
612                        } else {
613                            None
614                        }
615                    } else {
616                        None
617                    }
618                },
619        })
620    } else {
621        Err(anyhow::anyhow!("Expected struct fields"))
622    }
623}
624
625/// Decode AccessControlExtensionChanged event (0x01, priority: info)
626pub fn decode_access_control_extension_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<AccessControlExtensionChangedEvent> {
627    if let tlv::TlvItemValue::List(_fields) = inp {
628        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
629        Ok(AccessControlExtensionChangedEvent {
630                                admin_node_id: item.get_int(&[1]),
631                                admin_passcode_id: item.get_int(&[2]).map(|v| v as u16),
632                                change_type: item.get_int(&[3]).and_then(|v| ChangeType::from_u8(v as u8)),
633                                latest_value: {
634                    if let Some(nested_tlv) = item.get(&[4]) {
635                        if let tlv::TlvItemValue::List(_) = nested_tlv {
636                            let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
637                            Some(AccessControlExtension {
638                data: nested_item.get_octet_string_owned(&[1]),
639                            })
640                        } else {
641                            None
642                        }
643                    } else {
644                        None
645                    }
646                },
647        })
648    } else {
649        Err(anyhow::anyhow!("Expected struct fields"))
650    }
651}
652
653/// Decode FabricRestrictionReviewUpdate event (0x02, priority: info)
654pub fn decode_fabric_restriction_review_update_event(inp: &tlv::TlvItemValue) -> anyhow::Result<FabricRestrictionReviewUpdateEvent> {
655    if let tlv::TlvItemValue::List(_fields) = inp {
656        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
657        Ok(FabricRestrictionReviewUpdateEvent {
658                                token: item.get_int(&[0]),
659                                instruction: item.get_string_owned(&[1]),
660                                arl_request_flow_url: item.get_string_owned(&[2]),
661        })
662    } else {
663        Err(anyhow::anyhow!("Expected struct fields"))
664    }
665}
666