1use chrono::naive::NaiveDateTime;
6use fidl_fuchsia_bluetooth_map::{Audience, Notification, NotificationType};
7use objects::{Builder, ObexObjectError as Error, Parser};
8use std::collections::HashSet;
9use std::fmt;
10use std::str::FromStr;
11use xml::attribute::OwnedAttribute;
12use xml::reader::{ParserConfig, XmlEvent};
13use xml::writer::{EmitterConfig, XmlEvent as XmlWriteEvent};
14use xml::EventWriter;
15
16use crate::packets::{bool_to_string, str_to_bool, truncate_string, ISO_8601_TIME_FORMAT};
17use crate::MessageType;
18
19const EVENT_ELEM: &str = "event";
39const EVENT_REPORT_ELEM: &str = "MAP-event-report";
40
41const VERSION_ATTR: &str = "version";
42
43const TYPE_ATTR: &str = "type";
44const HANDLE_ATTR: &str = "handle";
45const FOLDER_ATTR: &str = "folder";
46const OLD_FOLDER_ATTR: &str = "old_folder";
47const MSG_TYPE_ATTR: &str = "msg_type";
48
49const DATETIME_ATTR: &str = "datetime";
51const SUBJECT_ATTR: &str = "subject";
52const SENDER_NAME_ATTR: &str = "sender_name";
53const PRIORITY_ATTR: &str = "priority";
54
55#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
59pub enum Type {
60 NewMessage,
61 DeliverySuccess,
62 SendingSuccess,
63 DeliveryFailure,
64 SendingFailure,
65 MessageDeleted,
66 MessageShift,
67 ReadStatusChanged,
68}
69
70impl fmt::Display for Type {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 let str_value = match self {
73 Type::NewMessage => "NewMessage",
74 Type::DeliverySuccess => "DeliverySuccess",
75 Type::SendingSuccess => "SendingSuccess",
76 Type::DeliveryFailure => "DeliveryFailure",
77 Type::SendingFailure => "SendingFailure",
78 Type::MessageDeleted => "MessageDeleted",
79 Type::MessageShift => "MessageShift",
80 Type::ReadStatusChanged => "ReadStatusChanged",
81 };
82
83 write!(f, "{}", str_value)
84 }
85}
86
87impl FromStr for Type {
88 type Err = Error;
89 fn from_str(src: &str) -> Result<Self, Self::Err> {
90 let val = match src {
91 "NewMessage" => Self::NewMessage,
92 "DeliverySuccess" => Self::DeliverySuccess,
93 "SendingSuccess" => Self::SendingSuccess,
94 "SendingFailure" => Self::DeliveryFailure,
95 "MessageDeleted" => Self::MessageDeleted,
96 "MessageShift" => Self::MessageShift,
97 "ReadStatusChanged" => Self::ReadStatusChanged,
98 v => return Err(Error::invalid_data(v)),
99 };
100 Ok(val)
101 }
102}
103
104impl From<Type> for NotificationType {
105 fn from(value: Type) -> Self {
106 match value {
107 Type::NewMessage => NotificationType::NewMessage,
108 Type::DeliverySuccess => NotificationType::DeliverySuccess,
109 Type::SendingSuccess => NotificationType::SendingSuccess,
110 Type::DeliveryFailure => NotificationType::DeliveryFailure,
111 Type::SendingFailure => NotificationType::SendingFailure,
112 Type::MessageDeleted => NotificationType::MessageDeleted,
113 Type::MessageShift => NotificationType::MessageShift,
114 Type::ReadStatusChanged => NotificationType::ReadStatusChanged,
115 }
116 }
117}
118
119#[derive(Clone, Copy, Debug, PartialEq)]
120pub enum EventReportVersion {
121 V1_0,
122 V1_1,
123}
124
125impl fmt::Display for EventReportVersion {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 match self {
128 Self::V1_0 => write!(f, "1.0"),
129 Self::V1_1 => write!(f, "1.1"),
130 }
131 }
132}
133
134impl FromStr for EventReportVersion {
135 type Err = Error;
136 fn from_str(src: &str) -> Result<Self, Self::Err> {
137 match src {
138 "1.0" => Ok(Self::V1_0),
139 "1.1" => Ok(Self::V1_1),
140 _v => Err(Error::UnsupportedVersion),
141 }
142 }
143}
144
145#[derive(Clone, Debug, Eq, Hash, PartialEq)]
148pub enum EventAttribute {
149 Type(Type),
151 Handle(u64),
152 Folder(String),
153 OldFolder(String),
154 MessageType(MessageType),
155 Datetime(NaiveDateTime),
157 Subject(String),
158 SenderName(String),
159 Priority(bool),
160}
161
162impl EventAttribute {
163 fn xml_attribute_name(&self) -> &'static str {
164 match self {
165 EventAttribute::Type(_) => TYPE_ATTR,
166 EventAttribute::Handle(_) => HANDLE_ATTR,
167 EventAttribute::Folder(_) => FOLDER_ATTR,
168 EventAttribute::OldFolder(_) => OLD_FOLDER_ATTR,
169 EventAttribute::MessageType(_) => MSG_TYPE_ATTR,
170 EventAttribute::Datetime(_) => DATETIME_ATTR,
171 EventAttribute::Subject(_) => SUBJECT_ATTR,
172 EventAttribute::SenderName(_) => SENDER_NAME_ATTR,
173 EventAttribute::Priority(_) => PRIORITY_ATTR,
174 }
175 }
176
177 fn xml_attribute_value(&self) -> String {
178 match self {
179 EventAttribute::Type(v) => v.to_string(),
180 EventAttribute::Handle(v) => hex::encode(v.to_be_bytes()),
181 EventAttribute::Folder(v) => v.into(),
182 EventAttribute::OldFolder(v) => v.into(),
183 EventAttribute::MessageType(v) => v.to_string(),
184 EventAttribute::Datetime(v) => v.format(ISO_8601_TIME_FORMAT).to_string(),
185 EventAttribute::Subject(v) => v.to_string(),
186 EventAttribute::SenderName(v) => v.to_string(),
187 EventAttribute::Priority(v) => bool_to_string(*v),
188 }
189 }
190
191 fn add_to_notification(&self, notification: &mut Notification) -> Result<(), Error> {
193 match self {
194 EventAttribute::Type(type_) => notification.type_ = Some((*type_).into()),
195 EventAttribute::Handle(h) => notification.message_handle = Some(*h),
196 EventAttribute::Folder(name) => notification.folder = Some(name.clone()),
197 EventAttribute::MessageType(type_) => {
199 notification.message_type = Some((*type_).into());
200 }
201 EventAttribute::Datetime(datetime) => {
202 let timestamp_nanos = datetime
203 .and_utc()
204 .timestamp_nanos_opt()
205 .ok_or_else(|| Error::invalid_data(datetime))?;
206 notification.timestamp = Some(timestamp_nanos);
207 }
208 EventAttribute::Subject(value) => notification.subject = Some(value.to_string()),
209 EventAttribute::SenderName(value) => {
210 notification.sender =
211 Some(Audience { name: Some(value.to_string()), ..Default::default() })
212 }
213 EventAttribute::Priority(value) => notification.priority = Some(*value),
214 _ => {}
215 };
216 Ok(())
217 }
218
219 fn version(&self) -> EventReportVersion {
221 match self {
222 &EventAttribute::Datetime(_)
223 | &EventAttribute::Subject(_)
224 | &EventAttribute::SenderName(_)
225 | &EventAttribute::Priority(_) => EventReportVersion::V1_1,
226 _other => EventReportVersion::V1_0,
227 }
228 }
229
230 fn is_new_message_attribute(&self) -> bool {
234 if self.version() == EventReportVersion::V1_1 {
235 return true;
236 }
237 false
238 }
239
240 fn validate_v1_0(attrs: &Vec<EventAttribute>) -> Result<(), Error> {
242 if let Some(a) = attrs.iter().find(|a| a.version() == EventReportVersion::V1_1) {
244 return Err(Error::invalid_data(a.xml_attribute_name()));
245 }
246 Ok(())
247 }
248}
249
250impl TryFrom<&OwnedAttribute> for EventAttribute {
251 type Error = Error;
252
253 fn try_from(src: &OwnedAttribute) -> Result<Self, Error> {
254 let attr_name = src.name.local_name.as_str();
255 let attribute = match attr_name {
256 TYPE_ATTR => Self::Type(str::parse(src.value.as_str())?),
257 HANDLE_ATTR => {
258 if src.value.len() > 16 {
259 return Err(Error::invalid_data(&src.value));
260 }
261 Self::Handle(u64::from_be_bytes(
262 hex::decode(format!("{:0>16}", src.value.as_str()))
263 .map_err(|e| Error::invalid_data(e))?
264 .as_slice()
265 .try_into()
266 .unwrap(),
267 ))
268 }
269 FOLDER_ATTR => Self::Folder(truncate_string(&src.value, 512)),
271 OLD_FOLDER_ATTR => Self::OldFolder(truncate_string(&src.value, 512)),
272 MSG_TYPE_ATTR => Self::MessageType(str::parse(src.value.as_str())?),
273 DATETIME_ATTR => Self::Datetime(
275 NaiveDateTime::parse_from_str(src.value.as_str(), ISO_8601_TIME_FORMAT)
276 .map_err(|e| Error::invalid_data(e))?,
277 ),
278 SUBJECT_ATTR => Self::Subject(truncate_string(&src.value, 256)),
279 SENDER_NAME_ATTR => Self::SenderName(truncate_string(&src.value, 256)),
280 PRIORITY_ATTR => Self::Priority(str_to_bool(&src.value)?),
281 val => return Err(Error::invalid_data(val)),
282 };
283 Ok(attribute)
284 }
285}
286
287#[derive(Debug, PartialEq)]
291pub struct EventReport {
292 version: EventReportVersion,
293 attrs: Vec<EventAttribute>,
294}
295
296impl EventReport {
297 pub fn v1_0(attrs: Vec<EventAttribute>) -> Result<Self, Error> {
299 EventReport::new(EventReportVersion::V1_0, attrs)
300 }
301
302 pub fn v1_1(attrs: Vec<EventAttribute>) -> Result<Self, Error> {
304 EventReport::new(EventReportVersion::V1_1, attrs)
305 }
306
307 fn new(version: EventReportVersion, attrs: Vec<EventAttribute>) -> Result<Self, Error> {
308 let event_report = EventReport { version, attrs };
309 event_report.validate()?;
310 Ok(event_report)
311 }
312
313 pub fn version(&self) -> EventReportVersion {
314 self.version
315 }
316
317 fn validate(&self) -> Result<(), Error> {
318 let type_ = self
321 .attrs
322 .iter()
323 .find_map(|a| if let EventAttribute::Type(t) = a { Some(t) } else { None })
324 .ok_or_else(|| Error::missing_data(TYPE_ATTR))?;
325
326 if type_ != &Type::MessageShift
328 && self.attrs.iter().any(|a| a.xml_attribute_name() == OLD_FOLDER_ATTR)
329 {
330 return Err(Error::invalid_data(
331 "old_folder attribute is only allowed for MessageShift event",
332 ));
333 }
334 match self.version {
335 EventReportVersion::V1_0 => EventAttribute::validate_v1_0(&self.attrs)?,
336 EventReportVersion::V1_1 => {
337 if type_ == &Type::NewMessage {
339 return Ok(());
340 }
341 if self.attrs.iter().any(|a| a.is_new_message_attribute()) {
344 return Err(Error::invalid_data(
345 "contains attribute only allowed in NewMessage event",
346 ));
347 }
348 }
349 }
350 Ok(())
351 }
352
353 fn check_map_event_report_element(element: XmlEvent) -> Result<EventReportVersion, Error> {
356 let XmlEvent::StartElement { ref name, ref attributes, .. } = element else {
357 return Err(Error::invalid_data(format!("{element:?}")));
358 };
359
360 if name.local_name != EVENT_REPORT_ELEM {
361 return Err(Error::invalid_data(&name.local_name));
362 }
363
364 let version_attr = &attributes
365 .iter()
366 .find(|a| a.name.local_name == VERSION_ATTR)
367 .ok_or_else(|| Error::MissingData(VERSION_ATTR.to_string()))?
368 .value;
369 str::parse(version_attr.as_str())
370 }
371
372 fn write<W: std::io::Write>(&self, writer: &mut EventWriter<W>) -> Result<(), Error> {
373 let mut builder = XmlWriteEvent::start_element(EVENT_ELEM);
374 let attr_pairs: Vec<_> =
375 self.attrs.iter().map(|a| (a.xml_attribute_name(), a.xml_attribute_value())).collect();
376 for a in &attr_pairs {
377 builder = builder.attr(a.0, a.1.as_str());
378 }
379 writer.write(builder)?;
380 Ok(writer.write(XmlWriteEvent::end_element())?)
381 }
382}
383
384impl TryFrom<(XmlEvent, EventReportVersion)> for EventReport {
385 type Error = Error;
386 fn try_from(src: (XmlEvent, EventReportVersion)) -> Result<Self, Error> {
387 let XmlEvent::StartElement { ref name, ref attributes, .. } = src.0 else {
388 return Err(Error::InvalidData(format!("{:?}", src)));
389 };
390 if name.local_name.as_str() != EVENT_ELEM {
391 return Err(Error::invalid_data(&name.local_name));
392 }
393 let mut seen_attrs: HashSet<_> = HashSet::new();
394 let mut attrs = vec![];
395
396 attributes.iter().try_for_each(|a| {
397 let attr = EventAttribute::try_from(a)?;
398 if !seen_attrs.insert(std::mem::discriminant(&attr)) {
399 return Err(Error::DuplicateData(a.name.local_name.to_string()));
400 }
401 attrs.push(attr);
402 Ok(())
403 })?;
404
405 let event_report = EventReport { version: src.1, attrs };
406 let _ = event_report.validate()?;
407 Ok(event_report)
408 }
409}
410
411impl TryFrom<&EventReport> for Notification {
412 type Error = Error;
413 fn try_from(value: &EventReport) -> Result<Self, Error> {
414 let mut notification = Notification::default();
415
416 for a in &value.attrs {
417 let _ = a.add_to_notification(&mut notification)?;
418 }
419 Ok(notification)
420 }
421}
422
423impl Parser for EventReport {
424 type Error = Error;
425
426 fn parse<R: std::io::prelude::Read>(buf: R) -> Result<Self, Self::Error> {
428 let mut reader = ParserConfig::new()
429 .ignore_comments(true)
430 .whitespace_to_characters(true)
431 .cdata_to_characters(true)
432 .trim_whitespace(true)
433 .create_reader(buf);
434
435 match reader.next() {
437 Ok(XmlEvent::StartDocument { .. }) => {}
438 Ok(element) => return Err(Error::InvalidData(format!("{:?}", element))),
439 Err(e) => return Err(Error::ReadXml(e)),
440 };
441
442 let xml_event = reader.next()?;
444 let version = EventReport::check_map_event_report_element(xml_event)?;
445
446 let xml_event: XmlEvent = reader.next()?;
448 let invalid_elem_err = Err(Error::InvalidData(format!("{:?}", xml_event)));
449 let event_report: EventReport = match xml_event {
450 XmlEvent::StartElement { ref name, .. } => match name.local_name.as_str() {
451 EVENT_ELEM => (xml_event, version).try_into()?,
452 _ => return invalid_elem_err,
453 },
454 _ => return invalid_elem_err,
455 };
456
457 let xml_event = reader.next()?;
459 match xml_event {
460 XmlEvent::EndElement { ref name } => {
461 if name.local_name.as_str() != EVENT_ELEM {
462 return Err(Error::InvalidData(format!("{:?}", xml_event)));
463 }
464 }
465 _ => return Err(Error::InvalidData(format!("{:?}", xml_event))),
466 };
467
468 let xml_event = reader.next()?;
470 match xml_event {
471 XmlEvent::EndElement { ref name } => {
472 if name.local_name.as_str() != EVENT_REPORT_ELEM {
473 return Err(Error::InvalidData(format!("{:?}", xml_event)));
474 }
475 }
476 _ => return Err(Error::InvalidData(format!("{:?}", xml_event))),
477 };
478
479 let xml_event = reader.next()?;
481 match xml_event {
482 XmlEvent::EndDocument { .. } => {}
483 _ => return Err(Error::InvalidData(format!("{:?}", xml_event))),
484 };
485
486 Ok(event_report)
487 }
488}
489
490impl Builder for EventReport {
491 type Error = Error;
492
493 fn mime_type(&self) -> String {
495 "application/xml".to_string()
496 }
497
498 fn build<W: std::io::Write>(&self, buf: W) -> core::result::Result<(), Self::Error> {
499 let _ = self.validate()?;
501
502 let mut w = EmitterConfig::new()
503 .write_document_declaration(true)
504 .perform_indent(true)
505 .create_writer(buf);
506
507 let version = self.version().to_string();
509 let event_report =
510 XmlWriteEvent::start_element(EVENT_REPORT_ELEM).attr(VERSION_ATTR, version.as_str());
511 w.write(event_report)?;
512
513 self.write(&mut w)?;
515
516 Ok(w.write(XmlWriteEvent::end_element())?)
518 }
519}
520
521#[cfg(test)]
522mod tests {
523 use super::*;
524
525 use chrono::{NaiveDate, NaiveTime};
526 use fidl_fuchsia_bluetooth_map as fidl_bt_map;
527
528 use std::fs;
529 use std::io::Cursor;
530
531 #[fuchsia::test]
532 fn event_report() {
533 let _event_report = EventReport::v1_0(vec![
534 EventAttribute::Type(Type::NewMessage),
535 EventAttribute::Handle(0x12345678),
536 EventAttribute::Folder("TELECOM/MSG/INBOX".to_string()),
537 EventAttribute::MessageType(MessageType::SmsCdma),
538 ])
539 .expect("should be created");
540
541 let _event_report = EventReport::v1_1(vec![
542 EventAttribute::Type(Type::NewMessage),
543 EventAttribute::Handle(0x12345678),
544 EventAttribute::Folder("TELECOM/MSG/INBOX".to_string()),
545 EventAttribute::MessageType(MessageType::SmsCdma),
546 EventAttribute::Subject("Hello".to_string()),
547 ])
548 .expect("should be created");
549 }
550
551 #[fuchsia::test]
552 fn event_report_fail() {
553 let _ = EventReport::v1_0(vec![
555 EventAttribute::Type(Type::DeliveryFailure),
556 EventAttribute::Handle(0x12345678),
557 EventAttribute::Folder("TELECOM/MSG/INBOX".to_string()),
558 EventAttribute::OldFolder("TELECOM/MSG/SOME".to_string()),
559 EventAttribute::MessageType(MessageType::SmsCdma),
560 ])
561 .expect_err("should fail");
562
563 let _ = EventReport::v1_1(vec![
565 EventAttribute::Type(Type::DeliveryFailure),
566 EventAttribute::Handle(0x12345678),
567 EventAttribute::Folder("TELECOM/MSG/INBOX".to_string()),
568 EventAttribute::MessageType(MessageType::SmsCdma),
569 EventAttribute::Subject("Hello".to_string()),
570 ])
571 .expect_err("should fail");
572 }
573
574 #[fuchsia::test]
575 fn parse_event_report_v1_0_success() {
576 const V1_0_TEST_FILE: &str = "/pkg/data/sample_event_report_v1_0.xml";
577 let bytes = fs::read(V1_0_TEST_FILE).expect("should be ok");
578 let report = EventReport::parse(Cursor::new(bytes)).expect("should be ok");
579 assert_eq!(report.version(), EventReportVersion::V1_0);
580 assert_eq!(
581 report.attrs,
582 vec![
583 EventAttribute::Type(Type::NewMessage),
584 EventAttribute::Handle(0x12345678),
585 EventAttribute::Folder("TELECOM/MSG/INBOX".to_string()),
586 EventAttribute::MessageType(MessageType::SmsCdma),
587 ]
588 );
589 }
590
591 #[fuchsia::test]
592 fn parse_event_report_v1_1_success() {
593 const V1_1_TEST_FILE: &str = "/pkg/data/sample_event_report_v1_1.xml";
594 let bytes = fs::read(V1_1_TEST_FILE).expect("should be ok");
595 let report = EventReport::parse(Cursor::new(bytes)).expect("should be ok");
596 assert_eq!(report.version(), EventReportVersion::V1_1);
597 assert_eq!(
598 report.attrs,
599 vec![
600 EventAttribute::Type(Type::NewMessage),
601 EventAttribute::Handle(0x12345678),
602 EventAttribute::Folder("TELECOM/MSG/INBOX".to_string()),
603 EventAttribute::MessageType(MessageType::SmsCdma),
604 EventAttribute::Subject("Hello".to_string()),
605 EventAttribute::Datetime(NaiveDateTime::new(
606 NaiveDate::from_ymd_opt(2011, 02, 21).unwrap(),
607 NaiveTime::from_hms_opt(13, 05, 10).unwrap(),
608 )),
609 EventAttribute::SenderName("Jamie".to_string()),
610 EventAttribute::Priority(true),
611 ]
612 );
613 }
614
615 #[fuchsia::test]
616 fn parse_event_report_fail() {
617 let bytes = fs::read("/pkg/data/bad_sample.xml").unwrap();
618 let Error::InvalidData(_) =
619 EventReport::parse(Cursor::new(bytes)).expect_err("should have failed")
620 else {
621 panic!("expected failure due to invalid data");
622 };
623
624 let bytes = fs::read("/pkg/data/sample_event_report_v1_2.xml").unwrap();
625 let Error::UnsupportedVersion =
626 EventReport::parse(Cursor::new(bytes)).expect_err("should have failed")
627 else {
628 panic!("expected failure due to unsupported version");
629 };
630
631 let bytes = fs::read("/pkg/data/bad_sample_event_report_v1_0_1.xml").unwrap();
632 let Error::ReadXml(_) =
633 EventReport::parse(Cursor::new(bytes)).expect_err("should have failed")
634 else {
635 panic!("expected failure due to read xml error");
636 };
637
638 let bytes = fs::read("/pkg/data/bad_sample_event_report_v1_0_2.xml").unwrap();
639 let Error::MissingData(_) =
640 EventReport::parse(Cursor::new(bytes)).expect_err("should have failed")
641 else {
642 panic!("expected failure due to missing data");
643 };
644
645 let bytes = fs::read("/pkg/data/bad_sample_event_report_v1_1.xml").unwrap();
646 let Error::InvalidData(_) =
647 EventReport::parse(Cursor::new(bytes)).expect_err("should have failed")
648 else {
649 panic!("expected failure due to invalid data");
650 };
651 }
652
653 #[fuchsia::test]
654 fn build_event_report_v1_0_success() {
655 let event_report = EventReport {
656 version: EventReportVersion::V1_0,
657 attrs: vec![
658 EventAttribute::Type(Type::NewMessage),
659 EventAttribute::Handle(12345678),
660 EventAttribute::Folder("TELECOM/MSG/INBOX".to_string()),
661 EventAttribute::MessageType(MessageType::SmsCdma),
662 ],
663 };
664
665 let mut buf = Vec::new();
666 let _ = event_report.build(&mut buf).expect("should have succeeded");
667 assert_eq!(
668 event_report,
669 EventReport::parse(Cursor::new(buf)).expect("should be valid xml")
670 );
671 }
672
673 #[fuchsia::test]
674 fn build_event_report_v1_1_success() {
675 let event_report = EventReport {
676 version: EventReportVersion::V1_1,
677 attrs: vec![
678 EventAttribute::Type(Type::NewMessage),
679 EventAttribute::Handle(12345678),
680 EventAttribute::Folder("TELECOM/MSG/INBOX".to_string()),
681 EventAttribute::MessageType(MessageType::SmsCdma),
682 EventAttribute::Subject("Hello".to_string()),
683 EventAttribute::Datetime(NaiveDateTime::new(
684 NaiveDate::from_ymd_opt(2011, 02, 21).unwrap(),
685 NaiveTime::from_hms_opt(13, 05, 10).unwrap(),
686 )),
687 EventAttribute::SenderName("Jamie".to_string()),
688 EventAttribute::Priority(true),
689 ],
690 };
691
692 let mut buf = Vec::new();
693 event_report.build(&mut buf).expect("should have succeeded");
694 assert_eq!(
695 event_report,
696 EventReport::parse(Cursor::new(buf)).expect("should be valid xml")
697 );
698 }
699
700 #[fuchsia::test]
701 fn build_event_report_fail() {
702 let event_report = EventReport {
704 version: EventReportVersion::V1_0,
705 attrs: vec![
706 EventAttribute::Handle(12345678),
707 EventAttribute::Folder("TELECOM/MSG/INBOX".to_string()),
708 EventAttribute::MessageType(MessageType::SmsCdma),
709 ],
710 };
711 let mut buf = Vec::new();
712 let _ = event_report.build(&mut buf).expect_err("should have failed");
713
714 let event_report = EventReport {
716 version: EventReportVersion::V1_1,
717 attrs: vec![
718 EventAttribute::Type(Type::DeliveryFailure),
719 EventAttribute::Handle(12345678),
720 EventAttribute::Folder("TELECOM/MSG/INBOX".to_string()),
721 EventAttribute::MessageType(MessageType::SmsCdma),
722 EventAttribute::Subject("Hello".to_string()),
723 ],
724 };
725 let mut buf = Vec::new();
726 let _ = event_report.build(&mut buf).expect_err("should have failed");
727 }
728
729 #[fuchsia::test]
730 fn notification() {
731 let event_report = EventReport::v1_1(vec![
732 EventAttribute::Type(Type::NewMessage),
733 EventAttribute::Handle(12345678),
734 EventAttribute::Folder("TELECOM/MSG/INBOX".to_string()),
735 EventAttribute::MessageType(MessageType::SmsCdma),
736 EventAttribute::Datetime(NaiveDateTime::new(
737 NaiveDate::from_ymd_opt(2000, 01, 09).unwrap(),
738 NaiveTime::from_hms_opt(01, 09, 19).unwrap(),
739 )),
740 ])
741 .unwrap();
742
743 let notification: Notification = (&event_report).try_into().expect("should succeed");
745 assert_eq!(
746 notification,
747 Notification {
748 type_: Some(NotificationType::NewMessage),
749 message_handle: Some(12345678),
750 folder: Some("TELECOM/MSG/INBOX".to_string()),
751 message_type: Some(fidl_bt_map::MessageType::SMS_CDMA),
752 timestamp: Some(947380159000000000),
753 ..Default::default()
754 }
755 );
756 }
757
758 #[fuchsia::test]
759 fn notification_fail() {
760 let event_report = EventReport::v1_1(vec![
761 EventAttribute::Type(Type::NewMessage),
762 EventAttribute::Handle(12345678),
763 EventAttribute::Folder("TELECOM/MSG/INBOX".to_string()),
764 EventAttribute::MessageType(MessageType::SmsCdma),
765 EventAttribute::Datetime(NaiveDateTime::new(
766 NaiveDate::from_ymd_opt(1500, 02, 21).unwrap(),
768 NaiveTime::from_hms_opt(13, 05, 10).unwrap(),
769 )),
770 ])
771 .unwrap();
772
773 let res: Result<Notification, _> = (&event_report).try_into();
775 let _ = res.expect_err("should have failed");
776 }
777}