use chrono::{DateTime, FixedOffset};
use imap_proto::types::{
AttributeValue, BodyStructure, Envelope, MessageSection, Response, SectionPath,
};
use super::{Flag, Seq, Uid};
use crate::types::ResponseData;
const DATE_TIME_FORMAT: &str = "%d-%b-%Y %H:%M:%S %z";
#[derive(Debug)]
pub struct Fetch {
response: ResponseData,
pub message: Seq,
pub uid: Option<Uid>,
pub size: Option<u32>,
}
impl Fetch {
pub(crate) fn new(response: ResponseData) -> Self {
let (message, uid, size) = if let Response::Fetch(message, attrs) = response.parsed() {
let mut uid = None;
let mut size = None;
for attr in attrs {
match attr {
AttributeValue::Uid(id) => uid = Some(*id),
AttributeValue::Rfc822Size(sz) => size = Some(*sz),
_ => {}
}
}
(*message, uid, size)
} else {
unreachable!()
};
Fetch {
response,
message,
uid,
size,
}
}
pub fn flags(&self) -> impl Iterator<Item = Flag<'_>> {
if let Response::Fetch(_, attrs) = self.response.parsed() {
attrs
.iter()
.filter_map(|attr| match attr {
AttributeValue::Flags(raw_flags) => {
Some(raw_flags.iter().map(|s| Flag::from(*s)))
}
_ => None,
})
.flatten()
} else {
unreachable!()
}
}
pub fn header(&self) -> Option<&[u8]> {
if let Response::Fetch(_, attrs) = self.response.parsed() {
attrs
.iter()
.filter_map(|av| match av {
AttributeValue::BodySection {
section: Some(SectionPath::Full(MessageSection::Header)),
data: Some(hdr),
..
}
| AttributeValue::Rfc822Header(Some(hdr)) => Some(*hdr),
_ => None,
})
.next()
} else {
unreachable!()
}
}
pub fn body(&self) -> Option<&[u8]> {
if let Response::Fetch(_, attrs) = self.response.parsed() {
attrs
.iter()
.filter_map(|av| match av {
AttributeValue::BodySection {
section: None,
data: Some(body),
..
}
| AttributeValue::Rfc822(Some(body)) => Some(*body),
_ => None,
})
.next()
} else {
unreachable!()
}
}
pub fn text(&self) -> Option<&[u8]> {
if let Response::Fetch(_, attrs) = self.response.parsed() {
attrs
.iter()
.filter_map(|av| match av {
AttributeValue::BodySection {
section: Some(SectionPath::Full(MessageSection::Text)),
data: Some(body),
..
}
| AttributeValue::Rfc822Text(Some(body)) => Some(*body),
_ => None,
})
.next()
} else {
unreachable!()
}
}
pub fn envelope(&self) -> Option<&Envelope<'_>> {
if let Response::Fetch(_, attrs) = self.response.parsed() {
attrs
.iter()
.filter_map(|av| match av {
AttributeValue::Envelope(env) => Some(&**env),
_ => None,
})
.next()
} else {
unreachable!()
}
}
pub fn section(&self, path: &SectionPath) -> Option<&[u8]> {
if let Response::Fetch(_, attrs) = self.response.parsed() {
attrs
.iter()
.filter_map(|av| match av {
AttributeValue::BodySection {
section: Some(sp),
data: Some(data),
..
} if sp == path => Some(*data),
_ => None,
})
.next()
} else {
unreachable!()
}
}
pub fn internal_date(&self) -> Option<DateTime<FixedOffset>> {
if let Response::Fetch(_, attrs) = self.response.parsed() {
attrs
.iter()
.filter_map(|av| match av {
AttributeValue::InternalDate(date_time) => Some(*date_time),
_ => None,
})
.next()
.and_then(
|date_time| match DateTime::parse_from_str(date_time, DATE_TIME_FORMAT) {
Ok(date_time) => Some(date_time),
Err(_) => None,
},
)
} else {
unreachable!()
}
}
pub fn bodystructure(&self) -> Option<&BodyStructure<'_>> {
if let Response::Fetch(_, attrs) = self.response.parsed() {
attrs
.iter()
.filter_map(|av| match av {
AttributeValue::BodyStructure(bs) => Some(bs),
_ => None,
})
.next()
} else {
unreachable!()
}
}
}