use std::fmt;
use obex_core::client::ObexError;
use obex_core::TransportError;
use quick_xml::{events::Event, Reader, XmlVersion};
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EventType {
NewMessage,
DeliverySuccess,
SendingSuccess,
DeliveryFailure,
SendingFailure,
MessageDeleted,
MessageShift,
MemoryFull,
MemoryAvailable,
ReadStatusChanged,
}
impl fmt::Display for EventType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::NewMessage => "NewMessage",
Self::DeliverySuccess => "DeliverySuccess",
Self::SendingSuccess => "SendingSuccess",
Self::DeliveryFailure => "DeliveryFailure",
Self::SendingFailure => "SendingFailure",
Self::MessageDeleted => "MessageDeleted",
Self::MessageShift => "MessageShift",
Self::MemoryFull => "MemoryFull",
Self::MemoryAvailable => "MemoryAvailable",
Self::ReadStatusChanged => "ReadStatusChanged",
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MnsEvent {
event_type: EventType,
handle: Option<String>,
folder: Option<String>,
old_folder: Option<String>,
msg_type: Option<String>,
datetime: Option<String>,
}
impl MnsEvent {
#[must_use]
pub const fn event_type(&self) -> EventType {
self.event_type
}
#[must_use]
pub fn handle(&self) -> Option<&str> {
self.handle.as_deref()
}
#[must_use]
pub fn folder(&self) -> Option<&str> {
self.folder.as_deref()
}
#[must_use]
pub fn old_folder(&self) -> Option<&str> {
self.old_folder.as_deref()
}
#[must_use]
pub fn msg_type(&self) -> Option<&str> {
self.msg_type.as_deref()
}
#[must_use]
pub fn datetime(&self) -> Option<&str> {
self.datetime.as_deref()
}
}
#[derive(Debug, Error)]
pub enum MnsError {
#[error("XML error: {0}")]
Parse(#[from] quick_xml::Error),
#[error("attribute error: {0}")]
Attr(#[from] quick_xml::events::attributes::AttrError),
#[error("unknown event type: {0}")]
UnknownEventType(String),
#[error("<event> element has no type attribute")]
MissingEventType,
#[error("MAP-event-report contained no <event> element")]
MissingEvent,
#[error("OBEX: {0}")]
Obex(#[from] ObexError),
#[error("transport: {0}")]
Transport(#[from] TransportError),
#[error("unexpected end of stream")]
UnexpectedEof,
#[error("unexpected OBEX opcode {0:#04x} from MNS client")]
UnexpectedOpcode(u8),
#[error("CONNECT Target UUID does not match MNS service UUID")]
InvalidTarget,
}
pub fn parse_event_report(xml: &[u8]) -> Result<MnsEvent, MnsError> {
let mut reader = Reader::from_reader(xml);
reader.config_mut().trim_text(true);
let mut buf = Vec::with_capacity(256);
loop {
match reader.read_event_into(&mut buf)? {
Event::Empty(e) | Event::Start(e) if e.name().as_ref() == b"event" => {
let mut raw_type: Option<String> = None;
let mut handle = None;
let mut folder = None;
let mut old_folder = None;
let mut msg_type = None;
let mut datetime = None;
for attr in e.attributes() {
let a = attr?;
let val = a.normalized_value(XmlVersion::Implicit1_0)?.into_owned();
match a.key.as_ref() {
b"type" => raw_type = Some(val),
b"handle" => handle = Some(val),
b"folder" => folder = Some(val),
b"old_folder" => old_folder = Some(val),
b"msg_type" => msg_type = Some(val),
b"datetime" => datetime = Some(val),
_ => {}
}
}
let event_type = match raw_type {
Some(ref s) => s.parse()?,
None => return Err(MnsError::MissingEventType),
};
return Ok(MnsEvent { event_type, handle, folder, old_folder, msg_type, datetime });
}
Event::Eof => return Err(MnsError::MissingEvent),
_ => {}
}
buf.clear();
}
}
impl std::str::FromStr for EventType {
type Err = MnsError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"NewMessage" => Ok(Self::NewMessage),
"DeliverySuccess" => Ok(Self::DeliverySuccess),
"SendingSuccess" => Ok(Self::SendingSuccess),
"DeliveryFailure" => Ok(Self::DeliveryFailure),
"SendingFailure" => Ok(Self::SendingFailure),
"MessageDeleted" => Ok(Self::MessageDeleted),
"MessageShift" => Ok(Self::MessageShift),
"MemoryFull" => Ok(Self::MemoryFull),
"MemoryAvailable" => Ok(Self::MemoryAvailable),
"ReadStatusChanged" => Ok(Self::ReadStatusChanged),
other => Err(MnsError::UnknownEventType(other.to_owned())),
}
}
}