use anyhow::{anyhow, Context, Error, Result};
use std::{convert::TryFrom, ops::Deref};
use crate::{
output::{PrintTable, PrintTableOpts, WriteColor},
ui::{Cell, Row, Table},
};
use super::{ImapFlag, ImapFlags};
#[derive(Debug, Default, serde::Serialize)]
pub struct ImapEnvelopes(pub Vec<ImapEnvelope>);
impl Deref for ImapEnvelopes {
type Target = Vec<ImapEnvelope>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl PrintTable for ImapEnvelopes {
fn print_table(&self, writter: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
writeln!(writter)?;
Table::print(writter, self, opts)?;
writeln!(writter)?;
Ok(())
}
}
#[derive(Debug, Default, Clone, serde::Serialize)]
pub struct ImapEnvelope {
pub id: u32,
pub flags: ImapFlags,
pub subject: String,
pub sender: String,
pub date: Option<String>,
}
impl Table for ImapEnvelope {
fn head() -> Row {
Row::new()
.cell(Cell::new("ID").bold().underline().white())
.cell(Cell::new("FLAGS").bold().underline().white())
.cell(Cell::new("SUBJECT").shrinkable().bold().underline().white())
.cell(Cell::new("SENDER").bold().underline().white())
.cell(Cell::new("DATE").bold().underline().white())
}
fn row(&self) -> Row {
let id = self.id.to_string();
let flags = self.flags.to_symbols_string();
let unseen = !self.flags.contains(&ImapFlag::Seen);
let subject = &self.subject;
let sender = &self.sender;
let date = self.date.as_deref().unwrap_or_default();
Row::new()
.cell(Cell::new(id).bold_if(unseen).red())
.cell(Cell::new(flags).bold_if(unseen).white())
.cell(Cell::new(subject).shrinkable().bold_if(unseen).green())
.cell(Cell::new(sender).bold_if(unseen).blue())
.cell(Cell::new(date).bold_if(unseen).yellow())
}
}
pub type RawImapEnvelopes = imap::types::ZeroCopy<Vec<RawImapEnvelope>>;
impl TryFrom<RawImapEnvelopes> for ImapEnvelopes {
type Error = Error;
fn try_from(raw_envelopes: RawImapEnvelopes) -> Result<Self, Self::Error> {
let mut envelopes = vec![];
for raw_envelope in raw_envelopes.iter().rev() {
envelopes.push(ImapEnvelope::try_from(raw_envelope).context("cannot parse envelope")?);
}
Ok(Self(envelopes))
}
}
pub type RawImapEnvelope = imap::types::Fetch;
impl TryFrom<&RawImapEnvelope> for ImapEnvelope {
type Error = Error;
fn try_from(fetch: &RawImapEnvelope) -> Result<ImapEnvelope> {
let envelope = fetch
.envelope()
.ok_or_else(|| anyhow!("cannot get envelope of message {}", fetch.message))?;
let id = fetch.message;
let flags = ImapFlags::try_from(fetch.flags())?;
let subject = envelope
.subject
.as_ref()
.map(|subj| {
rfc2047_decoder::decode(subj).context(format!(
"cannot decode subject of message {}",
fetch.message
))
})
.unwrap_or_else(|| Ok(String::default()))?;
let sender = envelope
.sender
.as_ref()
.and_then(|addrs| addrs.get(0))
.or_else(|| envelope.from.as_ref().and_then(|addrs| addrs.get(0)))
.ok_or_else(|| anyhow!("cannot get sender of message {}", fetch.message))?;
let sender = if let Some(ref name) = sender.name {
rfc2047_decoder::decode(&name.to_vec()).context(format!(
"cannot decode sender's name of message {}",
fetch.message,
))?
} else {
let mbox = sender
.mailbox
.as_ref()
.ok_or_else(|| anyhow!("cannot get sender's mailbox of message {}", fetch.message))
.and_then(|mbox| {
rfc2047_decoder::decode(&mbox.to_vec()).context(format!(
"cannot decode sender's mailbox of message {}",
fetch.message,
))
})?;
let host = sender
.host
.as_ref()
.ok_or_else(|| anyhow!("cannot get sender's host of message {}", fetch.message))
.and_then(|host| {
rfc2047_decoder::decode(&host.to_vec()).context(format!(
"cannot decode sender's host of message {}",
fetch.message,
))
})?;
format!("{}@{}", mbox, host)
};
let date = fetch
.internal_date()
.map(|date| date.naive_local().to_string());
Ok(Self {
id,
flags,
subject,
sender,
date,
})
}
}