1#![forbid(unsafe_code)]
11
12use crate::error::ImError;
13use crate::{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_reader(r: &mut TlvReader<'_>) -> Result<EventPath, ImError> {
211 let mut p = EventPath::default();
212 loop {
213 match r.next()? {
214 None => return Err(ImError::Codec(matter_codec::Error::UnclosedContainer)),
215 Some(Element::ContainerEnd) => return Ok(p),
216 Some(Element::Scalar {
217 tag: Tag::Context(0),
218 value: Value::Uint(n),
219 }) => p.node = Some(n),
220 Some(Element::Scalar {
221 tag: Tag::Context(1),
222 value: Value::Uint(n),
223 }) => p.endpoint = u16::try_from(n).ok(),
224 Some(Element::Scalar {
225 tag: Tag::Context(2),
226 value: Value::Uint(n),
227 }) => p.cluster = u32::try_from(n).ok(),
228 Some(Element::Scalar {
229 tag: Tag::Context(3),
230 value: Value::Uint(n),
231 }) => p.event = u32::try_from(n).ok(),
232 Some(Element::Scalar {
233 tag: Tag::Context(4),
234 value: Value::Bool(b),
235 }) => p.is_urgent = Some(b),
236 Some(Element::ContainerStart { .. }) => skip_container(r)?,
237 Some(_) => {}
238 }
239 }
240}
241
242fn parse_event_report_ib(r: &mut TlvReader<'_>) -> Result<Option<EventReport>, ImError> {
250 let mut out: Option<EventReport> = None;
251 loop {
252 match r.next()? {
253 None => return Err(ImError::Codec(matter_codec::Error::UnclosedContainer)),
254 Some(Element::ContainerEnd) => break,
255 Some(Element::ContainerStart {
257 tag: Tag::Context(1),
258 kind: ContainerKind::Structure,
259 }) => out = Some(EventReport::Data(parse_event_data(r)?)),
260 Some(Element::ContainerStart {
262 tag: Tag::Context(0),
263 kind: ContainerKind::Structure,
264 }) => out = Some(parse_event_status(r)?),
265 Some(Element::ContainerStart { .. }) => skip_container(r)?,
266 Some(_) => {}
267 }
268 }
269 Ok(out)
270}
271
272fn parse_event_data(r: &mut TlvReader<'_>) -> Result<EventReportItem, ImError> {
279 let mut path = EventPath::default();
280 let mut event_number = 0u64;
281 let mut priority = EventPriority::Unknown(0xFF);
282 let mut timestamp = EventTimestamp::None;
283 let mut value: Option<Value> = None;
284 loop {
285 match r.next()? {
286 None => return Err(ImError::Codec(matter_codec::Error::UnclosedContainer)),
287 Some(Element::ContainerEnd) => break,
288 Some(Element::ContainerStart {
290 tag: Tag::Context(0),
291 kind: ContainerKind::List,
292 }) => {
293 path = event_path_from_reader(r)?;
294 }
295 Some(Element::Scalar {
296 tag: Tag::Context(1),
297 value: Value::Uint(n),
298 }) => event_number = n,
299 Some(Element::Scalar {
300 tag: Tag::Context(2),
301 value: Value::Uint(n),
302 }) => priority = EventPriority::from_u8(u8::try_from(n).unwrap_or(0xFF)),
303 Some(Element::Scalar {
304 tag: Tag::Context(3),
305 value: Value::Uint(n),
306 }) => timestamp = EventTimestamp::Epoch(n),
307 Some(Element::Scalar {
308 tag: Tag::Context(4),
309 value: Value::Uint(n),
310 }) => timestamp = EventTimestamp::System(n),
311 Some(Element::Scalar {
312 tag: Tag::Context(5),
313 value: Value::Uint(n),
314 }) => timestamp = EventTimestamp::DeltaEpoch(n),
315 Some(Element::Scalar {
316 tag: Tag::Context(6),
317 value: Value::Uint(n),
318 }) => timestamp = EventTimestamp::DeltaSystem(n),
319 Some(Element::Scalar {
321 tag: Tag::Context(7),
322 value: v,
323 }) => value = Some(v),
324 Some(Element::ContainerStart {
325 tag: Tag::Context(7),
326 kind,
327 }) => value = Some(read_container_value(r, kind)?),
328 Some(Element::ContainerStart { .. }) => skip_container(r)?,
329 Some(_) => {}
330 }
331 }
332 Ok(EventReportItem {
333 path,
334 event_number,
335 priority,
336 timestamp,
337 value: value.ok_or(ImError::MissingField("EventData.Data"))?,
338 })
339}
340
341fn parse_event_status(r: &mut TlvReader<'_>) -> Result<EventReport, ImError> {
347 let mut path = EventPath::default();
348 let mut status = 0u8;
349 loop {
350 match r.next()? {
351 None => return Err(ImError::Codec(matter_codec::Error::UnclosedContainer)),
352 Some(Element::ContainerEnd) => break,
353 Some(Element::ContainerStart {
355 tag: Tag::Context(0),
356 kind: ContainerKind::List,
357 }) => {
358 path = event_path_from_reader(r)?;
359 }
360 Some(Element::ContainerStart {
362 tag: Tag::Context(1),
363 kind: ContainerKind::Structure,
364 }) => loop {
365 match r.next()? {
366 None => return Err(ImError::Codec(matter_codec::Error::UnclosedContainer)),
367 Some(Element::ContainerEnd) => break,
368 Some(Element::Scalar {
369 tag: Tag::Context(0),
370 value: Value::Uint(n),
371 }) => status = u8::try_from(n).unwrap_or(0),
372 Some(Element::ContainerStart { .. }) => skip_container(r)?,
373 Some(_) => {}
374 }
375 },
376 Some(Element::ContainerStart { .. }) => skip_container(r)?,
377 Some(_) => {}
378 }
379 }
380 Ok(EventReport::Status { path, status })
381}
382
383pub(crate) fn parse_event_reports(
390 r: &mut TlvReader<'_>,
391 out: &mut Vec<EventReport>,
392) -> Result<(), ImError> {
393 loop {
394 match r.next()? {
395 None => return Err(ImError::Codec(matter_codec::Error::UnclosedContainer)),
396 Some(Element::ContainerEnd) => return Ok(()),
397 Some(Element::ContainerStart {
398 kind: ContainerKind::Structure,
399 ..
400 }) => {
401 if let Some(rep) = parse_event_report_ib(r)? {
402 out.push(rep);
403 }
404 }
405 Some(Element::ContainerStart { .. }) => skip_container(r)?,
406 Some(_) => {}
407 }
408 }
409}
410
411#[cfg(test)]
412mod tests {
413 #![allow(clippy::unwrap_used, clippy::expect_used)] use super::*;
415 use matter_codec::{ContainerKind, Element, Tag, TlvReader, Value};
416
417 #[test]
418 fn event_path_encodes_as_list_with_tags_1_2_3() {
419 let mut buf = Vec::new();
420 let mut w = TlvWriter::new(&mut buf);
421 EventPath::concrete(0, 0x28, 0x00).write(&mut w).unwrap();
422 let mut r = TlvReader::new(&buf);
423 assert!(matches!(
425 r.next().unwrap(),
426 Some(Element::ContainerStart {
427 tag: Tag::Anonymous,
428 kind: ContainerKind::List
429 })
430 ));
431 assert!(matches!(
433 r.next().unwrap(),
434 Some(Element::Scalar {
435 tag: Tag::Context(1),
436 value: Value::Uint(0)
437 })
438 ));
439 assert!(matches!(
440 r.next().unwrap(),
441 Some(Element::Scalar {
442 tag: Tag::Context(2),
443 value: Value::Uint(0x28)
444 })
445 ));
446 assert!(matches!(
447 r.next().unwrap(),
448 Some(Element::Scalar {
449 tag: Tag::Context(3),
450 value: Value::Uint(0x00)
451 })
452 ));
453 }
454
455 #[test]
456 fn event_filter_encodes_as_struct() {
457 let mut buf = Vec::new();
458 let mut w = TlvWriter::new(&mut buf);
459 EventFilter::from_event_min(0).write(&mut w).unwrap();
460 let mut r = TlvReader::new(&buf);
461 assert!(matches!(
463 r.next().unwrap(),
464 Some(Element::ContainerStart {
465 tag: Tag::Anonymous,
466 kind: ContainerKind::Structure
467 })
468 ));
469 assert!(matches!(
470 r.next().unwrap(),
471 Some(Element::Scalar {
472 tag: Tag::Context(1),
473 value: Value::Uint(0)
474 })
475 ));
476 }
477
478 #[test]
479 fn parses_event_data_ib() {
480 let mut buf = Vec::new();
483 let mut w = TlvWriter::new(&mut buf);
484 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();
488 w.put_uint(Tag::Context(2), 0x28).unwrap();
489 w.put_uint(Tag::Context(3), 0x00).unwrap();
490 w.end_container().unwrap();
491 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();
496 w.end_container().unwrap();
497
498 let mut r = TlvReader::new(&buf);
499 assert!(matches!(
500 r.next().unwrap(),
501 Some(Element::ContainerStart { .. })
502 ));
503 let rep = parse_event_report_ib(&mut r).unwrap().unwrap();
504 match rep {
505 EventReport::Data(it) => {
506 assert_eq!(it.path.endpoint, Some(0));
507 assert_eq!(it.path.cluster, Some(0x28));
508 assert_eq!(it.path.event, Some(0x00));
509 assert_eq!(it.event_number, 1);
510 assert_eq!(it.priority, EventPriority::Critical);
511 assert_eq!(it.timestamp, EventTimestamp::Epoch(0));
512 assert_eq!(it.value, Value::Uint(7));
513 }
514 EventReport::Status { .. } => panic!("expected Data, got Status"),
515 }
516 }
517
518 #[test]
519 fn parses_event_status_ib() {
520 let mut buf = Vec::new();
523 let mut w = TlvWriter::new(&mut buf);
524 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();
528 w.put_uint(Tag::Context(2), 0x28).unwrap();
529 w.put_uint(Tag::Context(3), 0x02).unwrap();
530 w.end_container().unwrap();
531 w.start_structure(Tag::Context(1)).unwrap(); w.put_uint(Tag::Context(0), 0x86).unwrap(); w.end_container().unwrap();
534 w.end_container().unwrap();
535 w.end_container().unwrap();
536
537 let mut r = TlvReader::new(&buf);
538 assert!(matches!(
539 r.next().unwrap(),
540 Some(Element::ContainerStart { .. })
541 ));
542 let rep = parse_event_report_ib(&mut r).unwrap().unwrap();
543 match rep {
544 EventReport::Status { path, status } => {
545 assert_eq!(path.endpoint, Some(1));
546 assert_eq!(path.event, Some(0x02));
547 assert_eq!(status, 0x86);
548 }
549 EventReport::Data(_) => panic!("expected Status, got Data"),
550 }
551 }
552}