1#![forbid(unsafe_code)]
11
12use crate::error::ImError;
13use crate::{read_container_members, read_container_value, skip_container};
14use matter_codec::{ContainerKind, Element, Tag, TlvReader, TlvWriter, Value};
15
16#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
21pub struct EventPath {
22 pub node: Option<u64>,
24 pub endpoint: Option<u16>,
26 pub cluster: Option<u32>,
28 pub event: Option<u32>,
30 pub is_urgent: Option<bool>,
32}
33
34impl EventPath {
35 #[must_use]
37 pub fn concrete(endpoint: u16, cluster: u32, event: u32) -> Self {
38 Self {
39 node: None,
40 endpoint: Some(endpoint),
41 cluster: Some(cluster),
42 event: Some(event),
43 is_urgent: None,
44 }
45 }
46
47 #[must_use]
49 pub fn cluster(endpoint: u16, cluster: u32) -> Self {
50 Self {
51 node: None,
52 endpoint: Some(endpoint),
53 cluster: Some(cluster),
54 event: None,
55 is_urgent: None,
56 }
57 }
58
59 pub(crate) fn write(&self, w: &mut TlvWriter<'_>) -> Result<(), matter_codec::Error> {
64 w.start_list(Tag::Anonymous)?;
65 if let Some(n) = self.node {
66 w.put_uint(Tag::Context(0), n)?;
67 }
68 if let Some(e) = self.endpoint {
69 w.put_uint(Tag::Context(1), u64::from(e))?;
70 }
71 if let Some(c) = self.cluster {
72 w.put_uint(Tag::Context(2), u64::from(c))?;
73 }
74 if let Some(ev) = self.event {
75 w.put_uint(Tag::Context(3), u64::from(ev))?;
76 }
77 if let Some(u) = self.is_urgent {
78 w.put_bool(Tag::Context(4), u)?;
79 }
80 w.end_container()
81 }
82}
83
84#[derive(Copy, Clone, Debug, PartialEq, Eq)]
87pub struct EventFilter {
88 pub node: Option<u64>,
90 pub event_min: u64,
92}
93
94impl EventFilter {
95 #[must_use]
97 pub fn from_event_min(event_min: u64) -> Self {
98 Self {
99 node: None,
100 event_min,
101 }
102 }
103
104 pub(crate) fn write(&self, w: &mut TlvWriter<'_>) -> Result<(), matter_codec::Error> {
110 w.start_structure(Tag::Anonymous)?;
111 if let Some(n) = self.node {
112 w.put_uint(Tag::Context(0), n)?;
113 }
114 w.put_uint(Tag::Context(1), self.event_min)?;
115 w.end_container()
116 }
117}
118
119#[derive(Copy, Clone, Debug, PartialEq, Eq)]
122#[non_exhaustive]
123pub enum EventPriority {
124 Debug,
126 Info,
128 Critical,
130 Unknown(u8),
132}
133
134impl EventPriority {
135 #[must_use]
136 fn from_u8(v: u8) -> Self {
137 match v {
138 0 => Self::Debug,
139 1 => Self::Info,
140 2 => Self::Critical,
141 other => Self::Unknown(other),
142 }
143 }
144}
145
146#[derive(Copy, Clone, Debug, PartialEq, Eq)]
151#[non_exhaustive]
152pub enum EventTimestamp {
153 Epoch(u64),
155 System(u64),
157 DeltaEpoch(u64),
159 DeltaSystem(u64),
161 None,
163}
164
165#[derive(Clone, Debug, PartialEq)]
167#[non_exhaustive]
168pub struct EventReportItem {
169 pub path: EventPath,
171 pub event_number: u64,
173 pub priority: EventPriority,
175 pub timestamp: EventTimestamp,
177 pub value: Value,
179}
180
181#[derive(Clone, Debug, PartialEq)]
184#[non_exhaustive]
185pub enum EventReport {
186 Data(EventReportItem),
188 Status {
190 path: EventPath,
192 status: u8,
194 },
195}
196
197fn event_path_from_members(members: &[(Tag, Value)]) -> EventPath {
199 let mut p = EventPath::default();
200 for (tag, v) in members {
201 match (tag, v) {
202 (Tag::Context(0), Value::Uint(n)) => p.node = Some(*n),
203 (Tag::Context(1), Value::Uint(n)) => p.endpoint = u16::try_from(*n).ok(),
204 (Tag::Context(2), Value::Uint(n)) => p.cluster = u32::try_from(*n).ok(),
205 (Tag::Context(3), Value::Uint(n)) => p.event = u32::try_from(*n).ok(),
206 (Tag::Context(4), Value::Bool(b)) => p.is_urgent = Some(*b),
207 _ => {}
208 }
209 }
210 p
211}
212
213fn parse_event_report_ib(r: &mut TlvReader<'_>) -> Result<Option<EventReport>, ImError> {
221 let mut out: Option<EventReport> = None;
222 loop {
223 match r.next()? {
224 None => return Err(ImError::Codec(matter_codec::Error::UnclosedContainer)),
225 Some(Element::ContainerEnd) => break,
226 Some(Element::ContainerStart {
228 tag: Tag::Context(1),
229 kind: ContainerKind::Structure,
230 }) => out = Some(EventReport::Data(parse_event_data(r)?)),
231 Some(Element::ContainerStart {
233 tag: Tag::Context(0),
234 kind: ContainerKind::Structure,
235 }) => out = Some(parse_event_status(r)?),
236 Some(Element::ContainerStart { .. }) => skip_container(r)?,
237 Some(_) => {}
238 }
239 }
240 Ok(out)
241}
242
243fn parse_event_data(r: &mut TlvReader<'_>) -> Result<EventReportItem, ImError> {
250 let mut path = EventPath::default();
251 let mut event_number = 0u64;
252 let mut priority = EventPriority::Unknown(0xFF);
253 let mut timestamp = EventTimestamp::None;
254 let mut value: Option<Value> = None;
255 loop {
256 match r.next()? {
257 None => return Err(ImError::Codec(matter_codec::Error::UnclosedContainer)),
258 Some(Element::ContainerEnd) => break,
259 Some(Element::ContainerStart {
261 tag: Tag::Context(0),
262 kind: ContainerKind::List,
263 }) => {
264 let members = read_container_members(r)?;
265 path = event_path_from_members(&members);
266 }
267 Some(Element::Scalar {
268 tag: Tag::Context(1),
269 value: Value::Uint(n),
270 }) => event_number = n,
271 Some(Element::Scalar {
272 tag: Tag::Context(2),
273 value: Value::Uint(n),
274 }) => priority = EventPriority::from_u8(u8::try_from(n).unwrap_or(0xFF)),
275 Some(Element::Scalar {
276 tag: Tag::Context(3),
277 value: Value::Uint(n),
278 }) => timestamp = EventTimestamp::Epoch(n),
279 Some(Element::Scalar {
280 tag: Tag::Context(4),
281 value: Value::Uint(n),
282 }) => timestamp = EventTimestamp::System(n),
283 Some(Element::Scalar {
284 tag: Tag::Context(5),
285 value: Value::Uint(n),
286 }) => timestamp = EventTimestamp::DeltaEpoch(n),
287 Some(Element::Scalar {
288 tag: Tag::Context(6),
289 value: Value::Uint(n),
290 }) => timestamp = EventTimestamp::DeltaSystem(n),
291 Some(Element::Scalar {
293 tag: Tag::Context(7),
294 value: v,
295 }) => value = Some(v),
296 Some(Element::ContainerStart {
297 tag: Tag::Context(7),
298 kind,
299 }) => value = Some(read_container_value(r, kind)?),
300 Some(Element::ContainerStart { .. }) => skip_container(r)?,
301 Some(_) => {}
302 }
303 }
304 Ok(EventReportItem {
305 path,
306 event_number,
307 priority,
308 timestamp,
309 value: value.ok_or(ImError::MissingField("EventData.Data"))?,
310 })
311}
312
313fn parse_event_status(r: &mut TlvReader<'_>) -> Result<EventReport, ImError> {
319 let mut path = EventPath::default();
320 let mut status = 0u8;
321 loop {
322 match r.next()? {
323 None => return Err(ImError::Codec(matter_codec::Error::UnclosedContainer)),
324 Some(Element::ContainerEnd) => break,
325 Some(Element::ContainerStart {
327 tag: Tag::Context(0),
328 kind: ContainerKind::List,
329 }) => {
330 let members = read_container_members(r)?;
331 path = event_path_from_members(&members);
332 }
333 Some(Element::ContainerStart {
335 tag: Tag::Context(1),
336 kind: ContainerKind::Structure,
337 }) => {
338 for (tag, v) in read_container_members(r)? {
339 if let (Tag::Context(0), Value::Uint(n)) = (tag, v) {
340 status = u8::try_from(n).unwrap_or(0);
341 }
342 }
343 }
344 Some(Element::ContainerStart { .. }) => skip_container(r)?,
345 Some(_) => {}
346 }
347 }
348 Ok(EventReport::Status { path, status })
349}
350
351pub(crate) fn parse_event_reports(
358 r: &mut TlvReader<'_>,
359 out: &mut Vec<EventReport>,
360) -> Result<(), ImError> {
361 loop {
362 match r.next()? {
363 None => return Err(ImError::Codec(matter_codec::Error::UnclosedContainer)),
364 Some(Element::ContainerEnd) => return Ok(()),
365 Some(Element::ContainerStart {
366 kind: ContainerKind::Structure,
367 ..
368 }) => {
369 if let Some(rep) = parse_event_report_ib(r)? {
370 out.push(rep);
371 }
372 }
373 Some(Element::ContainerStart { .. }) => skip_container(r)?,
374 Some(_) => {}
375 }
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 #![allow(clippy::unwrap_used, clippy::expect_used)] use super::*;
383 use matter_codec::{ContainerKind, Element, Tag, TlvReader, Value};
384
385 #[test]
386 fn event_path_encodes_as_list_with_tags_1_2_3() {
387 let mut buf = Vec::new();
388 let mut w = TlvWriter::new(&mut buf);
389 EventPath::concrete(0, 0x28, 0x00).write(&mut w).unwrap();
390 let mut r = TlvReader::new(&buf);
391 assert!(matches!(
393 r.next().unwrap(),
394 Some(Element::ContainerStart {
395 tag: Tag::Anonymous,
396 kind: ContainerKind::List
397 })
398 ));
399 assert!(matches!(
401 r.next().unwrap(),
402 Some(Element::Scalar {
403 tag: Tag::Context(1),
404 value: Value::Uint(0)
405 })
406 ));
407 assert!(matches!(
408 r.next().unwrap(),
409 Some(Element::Scalar {
410 tag: Tag::Context(2),
411 value: Value::Uint(0x28)
412 })
413 ));
414 assert!(matches!(
415 r.next().unwrap(),
416 Some(Element::Scalar {
417 tag: Tag::Context(3),
418 value: Value::Uint(0x00)
419 })
420 ));
421 }
422
423 #[test]
424 fn event_filter_encodes_as_struct() {
425 let mut buf = Vec::new();
426 let mut w = TlvWriter::new(&mut buf);
427 EventFilter::from_event_min(0).write(&mut w).unwrap();
428 let mut r = TlvReader::new(&buf);
429 assert!(matches!(
431 r.next().unwrap(),
432 Some(Element::ContainerStart {
433 tag: Tag::Anonymous,
434 kind: ContainerKind::Structure
435 })
436 ));
437 assert!(matches!(
438 r.next().unwrap(),
439 Some(Element::Scalar {
440 tag: Tag::Context(1),
441 value: Value::Uint(0)
442 })
443 ));
444 }
445
446 #[test]
447 fn parses_event_data_ib() {
448 let mut buf = Vec::new();
451 let mut w = TlvWriter::new(&mut buf);
452 w.start_structure(Tag::Anonymous).unwrap(); w.start_structure(Tag::Context(1)).unwrap(); w.start_list(Tag::Context(0)).unwrap(); w.put_uint(Tag::Context(1), 0).unwrap();
456 w.put_uint(Tag::Context(2), 0x28).unwrap();
457 w.put_uint(Tag::Context(3), 0x00).unwrap();
458 w.end_container().unwrap();
459 w.put_uint(Tag::Context(1), 1).unwrap(); w.put_uint(Tag::Context(2), 2).unwrap(); w.put_uint(Tag::Context(3), 0).unwrap(); w.put_uint(Tag::Context(7), 7).unwrap(); w.end_container().unwrap();
464 w.end_container().unwrap();
465
466 let mut r = TlvReader::new(&buf);
467 assert!(matches!(
468 r.next().unwrap(),
469 Some(Element::ContainerStart { .. })
470 ));
471 let rep = parse_event_report_ib(&mut r).unwrap().unwrap();
472 match rep {
473 EventReport::Data(it) => {
474 assert_eq!(it.path.endpoint, Some(0));
475 assert_eq!(it.path.cluster, Some(0x28));
476 assert_eq!(it.path.event, Some(0x00));
477 assert_eq!(it.event_number, 1);
478 assert_eq!(it.priority, EventPriority::Critical);
479 assert_eq!(it.timestamp, EventTimestamp::Epoch(0));
480 assert_eq!(it.value, Value::Uint(7));
481 }
482 EventReport::Status { .. } => panic!("expected Data, got Status"),
483 }
484 }
485
486 #[test]
487 fn parses_event_status_ib() {
488 let mut buf = Vec::new();
491 let mut w = TlvWriter::new(&mut buf);
492 w.start_structure(Tag::Anonymous).unwrap(); w.start_structure(Tag::Context(0)).unwrap(); w.start_list(Tag::Context(0)).unwrap(); w.put_uint(Tag::Context(1), 1).unwrap();
496 w.put_uint(Tag::Context(2), 0x28).unwrap();
497 w.put_uint(Tag::Context(3), 0x02).unwrap();
498 w.end_container().unwrap();
499 w.start_structure(Tag::Context(1)).unwrap(); w.put_uint(Tag::Context(0), 0x86).unwrap(); w.end_container().unwrap();
502 w.end_container().unwrap();
503 w.end_container().unwrap();
504
505 let mut r = TlvReader::new(&buf);
506 assert!(matches!(
507 r.next().unwrap(),
508 Some(Element::ContainerStart { .. })
509 ));
510 let rep = parse_event_report_ib(&mut r).unwrap().unwrap();
511 match rep {
512 EventReport::Status { path, status } => {
513 assert_eq!(path.endpoint, Some(1));
514 assert_eq!(path.event, Some(0x02));
515 assert_eq!(status, 0x86);
516 }
517 EventReport::Data(_) => panic!("expected Status, got Data"),
518 }
519 }
520}