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)]
25#[non_exhaustive]
26pub struct EventPath {
27 pub node: Option<u64>,
29 pub endpoint: Option<u16>,
31 pub cluster: Option<u32>,
33 pub event: Option<u32>,
35 pub is_urgent: Option<bool>,
37}
38
39impl EventPath {
40 #[must_use]
42 pub fn concrete(endpoint: u16, cluster: u32, event: u32) -> Self {
43 Self {
44 node: None,
45 endpoint: Some(endpoint),
46 cluster: Some(cluster),
47 event: Some(event),
48 is_urgent: None,
49 }
50 }
51
52 #[must_use]
54 pub fn cluster(endpoint: u16, cluster: u32) -> Self {
55 Self {
56 node: None,
57 endpoint: Some(endpoint),
58 cluster: Some(cluster),
59 event: None,
60 is_urgent: None,
61 }
62 }
63
64 pub(crate) fn write(&self, w: &mut TlvWriter<'_>) -> Result<(), matter_codec::Error> {
69 w.start_list(Tag::Anonymous)?;
70 if let Some(n) = self.node {
71 w.put_uint(Tag::Context(0), n)?;
72 }
73 if let Some(e) = self.endpoint {
74 w.put_uint(Tag::Context(1), u64::from(e))?;
75 }
76 if let Some(c) = self.cluster {
77 w.put_uint(Tag::Context(2), u64::from(c))?;
78 }
79 if let Some(ev) = self.event {
80 w.put_uint(Tag::Context(3), u64::from(ev))?;
81 }
82 if let Some(u) = self.is_urgent {
83 w.put_bool(Tag::Context(4), u)?;
84 }
85 w.end_container()
86 }
87}
88
89#[derive(Copy, Clone, Debug, PartialEq, Eq)]
95#[non_exhaustive]
96pub struct EventFilter {
97 pub node: Option<u64>,
99 pub event_min: u64,
101}
102
103impl EventFilter {
104 #[must_use]
106 pub fn from_event_min(event_min: u64) -> Self {
107 Self {
108 node: None,
109 event_min,
110 }
111 }
112
113 pub(crate) fn write(&self, w: &mut TlvWriter<'_>) -> Result<(), matter_codec::Error> {
119 w.start_structure(Tag::Anonymous)?;
120 if let Some(n) = self.node {
121 w.put_uint(Tag::Context(0), n)?;
122 }
123 w.put_uint(Tag::Context(1), self.event_min)?;
124 w.end_container()
125 }
126}
127
128#[derive(Copy, Clone, Debug, PartialEq, Eq)]
131#[non_exhaustive]
132pub enum EventPriority {
133 Debug,
135 Info,
137 Critical,
139 Unknown(u8),
141}
142
143impl EventPriority {
144 #[must_use]
145 fn from_u8(v: u8) -> Self {
146 match v {
147 0 => Self::Debug,
148 1 => Self::Info,
149 2 => Self::Critical,
150 other => Self::Unknown(other),
151 }
152 }
153}
154
155#[derive(Copy, Clone, Debug, PartialEq, Eq)]
160#[non_exhaustive]
161pub enum EventTimestamp {
162 Epoch(u64),
164 System(u64),
166 DeltaEpoch(u64),
168 DeltaSystem(u64),
170 None,
172}
173
174#[derive(Clone, Debug, PartialEq)]
176#[non_exhaustive]
177pub struct EventReportItem {
178 pub path: EventPath,
180 pub event_number: u64,
182 pub priority: EventPriority,
184 pub timestamp: EventTimestamp,
186 pub value: Value,
188}
189
190#[derive(Clone, Debug, PartialEq)]
193#[non_exhaustive]
194pub enum EventReport {
195 Data(EventReportItem),
197 Status {
199 path: EventPath,
201 status: u8,
203 },
204}
205
206fn event_path_from_members(members: &[(Tag, Value)]) -> EventPath {
208 let mut p = EventPath::default();
209 for (tag, v) in members {
210 match (tag, v) {
211 (Tag::Context(0), Value::Uint(n)) => p.node = Some(*n),
212 (Tag::Context(1), Value::Uint(n)) => p.endpoint = u16::try_from(*n).ok(),
213 (Tag::Context(2), Value::Uint(n)) => p.cluster = u32::try_from(*n).ok(),
214 (Tag::Context(3), Value::Uint(n)) => p.event = u32::try_from(*n).ok(),
215 (Tag::Context(4), Value::Bool(b)) => p.is_urgent = Some(*b),
216 _ => {}
217 }
218 }
219 p
220}
221
222fn parse_event_report_ib(r: &mut TlvReader<'_>) -> Result<Option<EventReport>, ImError> {
230 let mut out: Option<EventReport> = None;
231 loop {
232 match r.next()? {
233 None => return Err(ImError::Codec(matter_codec::Error::UnclosedContainer)),
234 Some(Element::ContainerEnd) => break,
235 Some(Element::ContainerStart {
237 tag: Tag::Context(1),
238 kind: ContainerKind::Structure,
239 }) => out = Some(EventReport::Data(parse_event_data(r)?)),
240 Some(Element::ContainerStart {
242 tag: Tag::Context(0),
243 kind: ContainerKind::Structure,
244 }) => out = Some(parse_event_status(r)?),
245 Some(Element::ContainerStart { .. }) => skip_container(r)?,
246 Some(_) => {}
247 }
248 }
249 Ok(out)
250}
251
252fn parse_event_data(r: &mut TlvReader<'_>) -> Result<EventReportItem, ImError> {
259 let mut path = EventPath::default();
260 let mut event_number = 0u64;
261 let mut priority = EventPriority::Unknown(0xFF);
262 let mut timestamp = EventTimestamp::None;
263 let mut value: Option<Value> = None;
264 loop {
265 match r.next()? {
266 None => return Err(ImError::Codec(matter_codec::Error::UnclosedContainer)),
267 Some(Element::ContainerEnd) => break,
268 Some(Element::ContainerStart {
270 tag: Tag::Context(0),
271 kind: ContainerKind::List,
272 }) => {
273 let members = read_container_members(r)?;
274 path = event_path_from_members(&members);
275 }
276 Some(Element::Scalar {
277 tag: Tag::Context(1),
278 value: Value::Uint(n),
279 }) => event_number = n,
280 Some(Element::Scalar {
281 tag: Tag::Context(2),
282 value: Value::Uint(n),
283 }) => priority = EventPriority::from_u8(u8::try_from(n).unwrap_or(0xFF)),
284 Some(Element::Scalar {
285 tag: Tag::Context(3),
286 value: Value::Uint(n),
287 }) => timestamp = EventTimestamp::Epoch(n),
288 Some(Element::Scalar {
289 tag: Tag::Context(4),
290 value: Value::Uint(n),
291 }) => timestamp = EventTimestamp::System(n),
292 Some(Element::Scalar {
293 tag: Tag::Context(5),
294 value: Value::Uint(n),
295 }) => timestamp = EventTimestamp::DeltaEpoch(n),
296 Some(Element::Scalar {
297 tag: Tag::Context(6),
298 value: Value::Uint(n),
299 }) => timestamp = EventTimestamp::DeltaSystem(n),
300 Some(Element::Scalar {
302 tag: Tag::Context(7),
303 value: v,
304 }) => value = Some(v),
305 Some(Element::ContainerStart {
306 tag: Tag::Context(7),
307 kind,
308 }) => value = Some(read_container_value(r, kind)?),
309 Some(Element::ContainerStart { .. }) => skip_container(r)?,
310 Some(_) => {}
311 }
312 }
313 Ok(EventReportItem {
314 path,
315 event_number,
316 priority,
317 timestamp,
318 value: value.ok_or(ImError::MissingField("EventData.Data"))?,
319 })
320}
321
322fn parse_event_status(r: &mut TlvReader<'_>) -> Result<EventReport, ImError> {
328 let mut path = EventPath::default();
329 let mut status = 0u8;
330 loop {
331 match r.next()? {
332 None => return Err(ImError::Codec(matter_codec::Error::UnclosedContainer)),
333 Some(Element::ContainerEnd) => break,
334 Some(Element::ContainerStart {
336 tag: Tag::Context(0),
337 kind: ContainerKind::List,
338 }) => {
339 let members = read_container_members(r)?;
340 path = event_path_from_members(&members);
341 }
342 Some(Element::ContainerStart {
344 tag: Tag::Context(1),
345 kind: ContainerKind::Structure,
346 }) => {
347 for (tag, v) in read_container_members(r)? {
348 if let (Tag::Context(0), Value::Uint(n)) = (tag, v) {
349 status = u8::try_from(n).unwrap_or(0);
350 }
351 }
352 }
353 Some(Element::ContainerStart { .. }) => skip_container(r)?,
354 Some(_) => {}
355 }
356 }
357 Ok(EventReport::Status { path, status })
358}
359
360pub(crate) fn parse_event_reports(
367 r: &mut TlvReader<'_>,
368 out: &mut Vec<EventReport>,
369) -> Result<(), ImError> {
370 loop {
371 match r.next()? {
372 None => return Err(ImError::Codec(matter_codec::Error::UnclosedContainer)),
373 Some(Element::ContainerEnd) => return Ok(()),
374 Some(Element::ContainerStart {
375 kind: ContainerKind::Structure,
376 ..
377 }) => {
378 if let Some(rep) = parse_event_report_ib(r)? {
379 out.push(rep);
380 }
381 }
382 Some(Element::ContainerStart { .. }) => skip_container(r)?,
383 Some(_) => {}
384 }
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 #![allow(clippy::unwrap_used, clippy::expect_used)] use super::*;
392 use matter_codec::{ContainerKind, Element, Tag, TlvReader, Value};
393
394 #[test]
395 fn event_path_encodes_as_list_with_tags_1_2_3() {
396 let mut buf = Vec::new();
397 let mut w = TlvWriter::new(&mut buf);
398 EventPath::concrete(0, 0x28, 0x00).write(&mut w).unwrap();
399 let mut r = TlvReader::new(&buf);
400 assert!(matches!(
402 r.next().unwrap(),
403 Some(Element::ContainerStart {
404 tag: Tag::Anonymous,
405 kind: ContainerKind::List
406 })
407 ));
408 assert!(matches!(
410 r.next().unwrap(),
411 Some(Element::Scalar {
412 tag: Tag::Context(1),
413 value: Value::Uint(0)
414 })
415 ));
416 assert!(matches!(
417 r.next().unwrap(),
418 Some(Element::Scalar {
419 tag: Tag::Context(2),
420 value: Value::Uint(0x28)
421 })
422 ));
423 assert!(matches!(
424 r.next().unwrap(),
425 Some(Element::Scalar {
426 tag: Tag::Context(3),
427 value: Value::Uint(0x00)
428 })
429 ));
430 }
431
432 #[test]
433 fn event_filter_encodes_as_struct() {
434 let mut buf = Vec::new();
435 let mut w = TlvWriter::new(&mut buf);
436 EventFilter::from_event_min(0).write(&mut w).unwrap();
437 let mut r = TlvReader::new(&buf);
438 assert!(matches!(
440 r.next().unwrap(),
441 Some(Element::ContainerStart {
442 tag: Tag::Anonymous,
443 kind: ContainerKind::Structure
444 })
445 ));
446 assert!(matches!(
447 r.next().unwrap(),
448 Some(Element::Scalar {
449 tag: Tag::Context(1),
450 value: Value::Uint(0)
451 })
452 ));
453 }
454
455 #[test]
456 fn parses_event_data_ib() {
457 let mut buf = Vec::new();
460 let mut w = TlvWriter::new(&mut buf);
461 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();
465 w.put_uint(Tag::Context(2), 0x28).unwrap();
466 w.put_uint(Tag::Context(3), 0x00).unwrap();
467 w.end_container().unwrap();
468 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();
473 w.end_container().unwrap();
474
475 let mut r = TlvReader::new(&buf);
476 assert!(matches!(
477 r.next().unwrap(),
478 Some(Element::ContainerStart { .. })
479 ));
480 let rep = parse_event_report_ib(&mut r).unwrap().unwrap();
481 match rep {
482 EventReport::Data(it) => {
483 assert_eq!(it.path.endpoint, Some(0));
484 assert_eq!(it.path.cluster, Some(0x28));
485 assert_eq!(it.path.event, Some(0x00));
486 assert_eq!(it.event_number, 1);
487 assert_eq!(it.priority, EventPriority::Critical);
488 assert_eq!(it.timestamp, EventTimestamp::Epoch(0));
489 assert_eq!(it.value, Value::Uint(7));
490 }
491 EventReport::Status { .. } => panic!("expected Data, got Status"),
492 }
493 }
494
495 #[test]
496 fn parses_event_status_ib() {
497 let mut buf = Vec::new();
500 let mut w = TlvWriter::new(&mut buf);
501 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();
505 w.put_uint(Tag::Context(2), 0x28).unwrap();
506 w.put_uint(Tag::Context(3), 0x02).unwrap();
507 w.end_container().unwrap();
508 w.start_structure(Tag::Context(1)).unwrap(); w.put_uint(Tag::Context(0), 0x86).unwrap(); w.end_container().unwrap();
511 w.end_container().unwrap();
512 w.end_container().unwrap();
513
514 let mut r = TlvReader::new(&buf);
515 assert!(matches!(
516 r.next().unwrap(),
517 Some(Element::ContainerStart { .. })
518 ));
519 let rep = parse_event_report_ib(&mut r).unwrap().unwrap();
520 match rep {
521 EventReport::Status { path, status } => {
522 assert_eq!(path.endpoint, Some(1));
523 assert_eq!(path.event, Some(0x02));
524 assert_eq!(status, 0x86);
525 }
526 EventReport::Data(_) => panic!("expected Status, got Data"),
527 }
528 }
529}