bt_map/packets/
event_report.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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
19// From MAP v1.4.2 section 3.1.7 Event-Report Object:
20//
21// <!DTD for the OBEX MAP-Event-Report object--> <!DOCTYPE MAP-eventreport [
22// <!ELEMENT MAP-event-report ( event ) >
23// <!ATTLIST MAP-event-report version CDATA #FIXED "1.1">
24// <!ELEMENT event EMPTY>
25// <!ATTLIST event
26// type CDATA #REQUIRED
27// handle CDATA #IMPLIED
28// folder CDATA #IMPLIED
29// old_folder CDATA #IMPLIED
30// msg_type CDATA #IMPLIED
31// datetime CDATA #IMPLIED
32// subject CDATA #IMPLIED
33// sender_name CDATA #IMPLIED
34// priority CDATA #IMPLIED
35// >
36// ]>
37
38const 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
49// V1.1 specific attributes.
50const DATETIME_ATTR: &str = "datetime";
51const SUBJECT_ATTR: &str = "subject";
52const SENDER_NAME_ATTR: &str = "sender_name";
53const PRIORITY_ATTR: &str = "priority";
54
55/// Type of MAP event report. See MAP v1.4.2 Table 3.2 for details.
56/// Not all event report types form the spec are represented since
57/// we don't expect to observe the events.
58#[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/// List of attributes available for event-report objects.
146/// See MAP v1.4.2 section 3.1.7 for details.
147#[derive(Clone, Debug, Eq, Hash, PartialEq)]
148pub enum EventAttribute {
149    // Attributes that exist in EventReport v1.0 and above.
150    Type(Type),
151    Handle(u64),
152    Folder(String),
153    OldFolder(String),
154    MessageType(MessageType),
155    // Attributes that exist in EventReport v1.1 and above.
156    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    /// Adds this attribute to notification.
192    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            // Attributes that aren't represented in FIDL struct are ignored.
198            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    /// Returns the event report version the attribute was introduced in.
220    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    /// Returns whether or not the event attribute is an attribute that's
231    /// only allowed to exist for NewMessage event type.
232    // See MAP v1.4.2 section 3.1.7 for details.
233    fn is_new_message_attribute(&self) -> bool {
234        if self.version() == EventReportVersion::V1_1 {
235            return true;
236        }
237        false
238    }
239
240    /// Validate that all the attributes are valid for event report v1.0.
241    fn validate_v1_0(attrs: &Vec<EventAttribute>) -> Result<(), Error> {
242        // Make sure there are no v1.1 attributes.
243        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            // Shall be restricted to 512 bytes
270            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            // TODO(b/348051261): Support UTC Offset Timestamp Format feature.
274            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/// Represents a MAP-Event-Report object as defined in MAP v1.4.2 section 3.1.7.
288/// The object contains one event element which contains attributes that
289/// describes the event.
290#[derive(Debug, PartialEq)]
291pub struct EventReport {
292    version: EventReportVersion,
293    attrs: Vec<EventAttribute>,
294}
295
296impl EventReport {
297    /// Creates a new v1.0 message.
298    pub fn v1_0(attrs: Vec<EventAttribute>) -> Result<Self, Error> {
299        EventReport::new(EventReportVersion::V1_0, attrs)
300    }
301
302    /// Creates a new v1.1 message.
303    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        // See MAP v1.4.2 section 3.1.7 for details.
319        // Type is a required attribute.
320        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        // Old folder attribute is only allowed if event type is message shift.
327        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 new message event, we have no further checks.
338                if type_ == &Type::NewMessage {
339                    return Ok(());
340                }
341                // As per MAP v1.4.2 section 3.1.7 certain attributes are
342                // not allowed if not for new message event.
343                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    // Given the XML StartElement, checks whether or not it is a valid
354    // `MAP-event-report` element.
355    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    /// Parses EventReport from raw bytes of XML data.
427    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        // Process start of document.
436        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        // Process start of `MAP-event-report` element.
443        let xml_event = reader.next()?;
444        let version = EventReport::check_map_event_report_element(xml_event)?;
445
446        // Process start of `event` element.
447        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        // Process end of `event` element.
458        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        // Process end of `MAP-event-report` element.
469        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        // Process end of document.
480        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    // Returns the document type of the raw bytes of data.
494    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        // Only build the XML if the event report is a valid event report.
500        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        // Begin `MAP-event-report` element.
508        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        // Write `event` element.
514        self.write(&mut w)?;
515
516        // End `MAP-event-report` element.
517        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        // Old folder attr not allowed.
554        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        // Subject attr not allowed for non NewMessage type.
564        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        // Invalid v1.0 since missing Type attr.
703        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        // Invalid v1.1 since incompatible Type and Subject attrs.
715        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        // Should be converted successfully.
744        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                // Out of range date time.
767                NaiveDate::from_ymd_opt(1500, 02, 21).unwrap(),
768                NaiveTime::from_hms_opt(13, 05, 10).unwrap(),
769            )),
770        ])
771        .unwrap();
772
773        // Conversion should fail.
774        let res: Result<Notification, _> = (&event_report).try_into();
775        let _ = res.expect_err("should have failed");
776    }
777}