mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-05 17:15:12 +00:00
extract flags from msg model
This commit is contained in:
parent
563f9a6538
commit
e108729491
|
@ -7,7 +7,7 @@ use crate::{
|
|||
flag::cli::{flags_matches, flags_subcommand},
|
||||
imap::{self, ImapConnector},
|
||||
input,
|
||||
msg::{self, Attachments, Msg, ReadableMsg},
|
||||
msg::{self, Attachments, Msg, Msgs, ReadableMsg},
|
||||
output::{self, print},
|
||||
smtp,
|
||||
};
|
||||
|
@ -241,6 +241,8 @@ impl<'a> App<'a> {
|
|||
let page: u32 = matches.value_of("page").unwrap().parse().unwrap();
|
||||
|
||||
let msgs = imap_conn.list_msgs(&mbox, &page_size, &page)?;
|
||||
let msgs = Msgs::from(&msgs);
|
||||
|
||||
print(&output_type, msgs)?;
|
||||
|
||||
imap_conn.logout();
|
||||
|
@ -279,6 +281,8 @@ impl<'a> App<'a> {
|
|||
.join(" ");
|
||||
|
||||
let msgs = imap_conn.search_msgs(&mbox, &query, &page_size, &page)?;
|
||||
let msgs = Msgs::from(&msgs);
|
||||
|
||||
print(&output_type, msgs)?;
|
||||
|
||||
imap_conn.logout();
|
||||
|
|
|
@ -30,7 +30,8 @@ fn flags_arg<'a, 'b>() -> Arg<'a, 'b> {
|
|||
|
||||
pub fn flags_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||
SubCommand::with_name("flags")
|
||||
.aliases(&["flag", "f"])
|
||||
.aliases(&["flag", "fg"])
|
||||
.about("Manages flags")
|
||||
.subcommand(
|
||||
SubCommand::with_name("set")
|
||||
.aliases(&["s"])
|
||||
|
|
86
src/flag/model.rs
Normal file
86
src/flag/model.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
pub(crate) use imap::types::Flag;
|
||||
use serde::ser::{Serialize, SerializeSeq, Serializer};
|
||||
use std::ops::Deref;
|
||||
|
||||
// Serializable wrapper for `imap::types::Flag`
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct SerializableFlag<'f>(&'f imap::types::Flag<'f>);
|
||||
|
||||
impl<'f> Serialize for SerializableFlag<'f> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(match &self.0 {
|
||||
Flag::Seen => "Seen",
|
||||
Flag::Answered => "Answered",
|
||||
Flag::Flagged => "Flagged",
|
||||
Flag::Deleted => "Deleted",
|
||||
Flag::Draft => "Draft",
|
||||
Flag::Recent => "Recent",
|
||||
Flag::MayCreate => "MayCreate",
|
||||
Flag::Custom(cow) => cow,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Flags
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Flags<'f>(&'f [Flag<'f>]);
|
||||
|
||||
impl<'f> Flags<'f> {
|
||||
pub fn new(flags: &'f [imap::types::Flag<'f>]) -> Self {
|
||||
Self(flags)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'f> ToString for Flags<'f> {
|
||||
fn to_string(&self) -> String {
|
||||
let mut flags = String::new();
|
||||
|
||||
flags.push_str(if self.0.contains(&Flag::Seen) {
|
||||
" "
|
||||
} else {
|
||||
"🟓"
|
||||
});
|
||||
|
||||
flags.push_str(if self.0.contains(&Flag::Answered) {
|
||||
"↩"
|
||||
} else {
|
||||
" "
|
||||
});
|
||||
|
||||
flags.push_str(if self.0.contains(&Flag::Flagged) {
|
||||
"!"
|
||||
} else {
|
||||
" "
|
||||
});
|
||||
|
||||
flags
|
||||
}
|
||||
}
|
||||
|
||||
impl<'f> Deref for Flags<'f> {
|
||||
type Target = &'f [Flag<'f>];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'f> Serialize for Flags<'f> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
|
||||
|
||||
for flag in self.0 {
|
||||
seq.serialize_element(&SerializableFlag(flag))?;
|
||||
}
|
||||
|
||||
seq.end()
|
||||
}
|
||||
}
|
52
src/imap.rs
52
src/imap.rs
|
@ -6,7 +6,7 @@ use std::net::TcpStream;
|
|||
use crate::{
|
||||
config::{self, Account, Config},
|
||||
mbox::{Mbox, Mboxes},
|
||||
msg::{Msg, Msgs},
|
||||
msg::Msg,
|
||||
};
|
||||
|
||||
error_chain! {
|
||||
|
@ -21,8 +21,8 @@ pub struct ImapConnector<'a> {
|
|||
pub sess: imap::Session<TlsStream<TcpStream>>,
|
||||
}
|
||||
|
||||
impl<'a> ImapConnector<'a> {
|
||||
pub fn new(account: &'a Account) -> Result<Self> {
|
||||
impl<'ic> ImapConnector<'ic> {
|
||||
pub fn new(account: &'ic Account) -> Result<Self> {
|
||||
let tls = TlsConnector::new().chain_err(|| "Cannot create TLS connector")?;
|
||||
let client = if account.imap_starttls() {
|
||||
imap::connect_starttls(account.imap_addr(), &account.imap_host, &tls)
|
||||
|
@ -104,17 +104,18 @@ impl<'a> ImapConnector<'a> {
|
|||
|
||||
if let Some(seq) = self.last_new_seq()? {
|
||||
if prev_seq != seq {
|
||||
if let Some(msg) = self
|
||||
let msgs = self
|
||||
.sess
|
||||
.uid_fetch(seq.to_string(), "(ENVELOPE)")
|
||||
.chain_err(|| "Cannot fetch enveloppe")?
|
||||
.chain_err(|| "Cannot fetch enveloppe")?;
|
||||
let msg = msgs
|
||||
.iter()
|
||||
.next()
|
||||
.map(Msg::from)
|
||||
{
|
||||
config.run_notify_cmd(&msg.subject, &msg.sender)?;
|
||||
prev_seq = seq;
|
||||
}
|
||||
.ok_or_else(|| "Cannot fetch first message")
|
||||
.map(Msg::from)?;
|
||||
|
||||
config.run_notify_cmd(&msg.subject, &msg.sender)?;
|
||||
prev_seq = seq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +133,12 @@ impl<'a> ImapConnector<'a> {
|
|||
Ok(Mboxes(mboxes))
|
||||
}
|
||||
|
||||
pub fn list_msgs(&mut self, mbox: &str, page_size: &u32, page: &u32) -> Result<Msgs> {
|
||||
pub fn list_msgs(
|
||||
&mut self,
|
||||
mbox: &str,
|
||||
page_size: &u32,
|
||||
page: &u32,
|
||||
) -> Result<imap::types::ZeroCopy<Vec<imap::types::Fetch>>> {
|
||||
let last_seq = self
|
||||
.sess
|
||||
.select(mbox)
|
||||
|
@ -149,16 +155,12 @@ impl<'a> ImapConnector<'a> {
|
|||
let end = begin - begin.min(*page_size as i64) + 1;
|
||||
let range = format!("{}:{}", begin, end);
|
||||
|
||||
let msgs = self
|
||||
let fetches = self
|
||||
.sess
|
||||
.fetch(range, "(UID FLAGS ENVELOPE INTERNALDATE)")
|
||||
.chain_err(|| "Cannot fetch messages")?
|
||||
.iter()
|
||||
.rev()
|
||||
.map(Msg::from)
|
||||
.collect::<Vec<_>>();
|
||||
.chain_err(|| "Cannot fetch messages")?;
|
||||
|
||||
Ok(Msgs(msgs))
|
||||
Ok(fetches)
|
||||
}
|
||||
|
||||
pub fn search_msgs(
|
||||
|
@ -167,7 +169,7 @@ impl<'a> ImapConnector<'a> {
|
|||
query: &str,
|
||||
page_size: &usize,
|
||||
page: &usize,
|
||||
) -> Result<Msgs> {
|
||||
) -> Result<imap::types::ZeroCopy<Vec<imap::types::Fetch>>> {
|
||||
self.sess
|
||||
.select(mbox)
|
||||
.chain_err(|| format!("Cannot select mailbox `{}`", mbox))?;
|
||||
|
@ -183,15 +185,15 @@ impl<'a> ImapConnector<'a> {
|
|||
.collect::<Vec<_>>();
|
||||
let range = uids[begin..end.min(uids.len())].join(",");
|
||||
|
||||
let msgs = self
|
||||
let fetches = self
|
||||
.sess
|
||||
.fetch(&range, "(UID FLAGS ENVELOPE INTERNALDATE)")
|
||||
.chain_err(|| format!("Cannot fetch range `{}`", &range))?
|
||||
.iter()
|
||||
.map(Msg::from)
|
||||
.collect::<Vec<_>>();
|
||||
.chain_err(|| format!("Cannot fetch range `{}`", &range))?;
|
||||
// .iter()
|
||||
// .map(|fetch| Msg::from(fetch))
|
||||
// .collect::<Vec<_>>();
|
||||
|
||||
Ok(Msgs(msgs))
|
||||
Ok(fetches)
|
||||
}
|
||||
|
||||
pub fn read_msg(&mut self, mbox: &str, uid: &str) -> Result<Vec<u8>> {
|
||||
|
|
|
@ -9,6 +9,7 @@ mod smtp;
|
|||
mod table;
|
||||
mod flag {
|
||||
pub(crate) mod cli;
|
||||
pub(crate) mod model;
|
||||
}
|
||||
|
||||
use crate::app::App;
|
||||
|
|
101
src/msg.rs
101
src/msg.rs
|
@ -10,6 +10,7 @@ use std::{fmt, result};
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::config::{Account, Config};
|
||||
use crate::flag::model::{Flag, Flags};
|
||||
use crate::table::{self, DisplayRow, DisplayTable};
|
||||
|
||||
error_chain! {
|
||||
|
@ -170,29 +171,29 @@ impl<'a> ReadableMsg {
|
|||
|
||||
// Message
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Flag {
|
||||
Seen,
|
||||
Answered,
|
||||
Flagged,
|
||||
}
|
||||
// #[derive(Debug, Serialize, PartialEq)]
|
||||
// #[serde(rename_all = "lowercase")]
|
||||
// pub enum Flag {
|
||||
// Seen,
|
||||
// Answered,
|
||||
// Flagged,
|
||||
// }
|
||||
|
||||
impl Flag {
|
||||
fn from_imap_flag(flag: &imap::types::Flag<'_>) -> Option<Self> {
|
||||
match flag {
|
||||
imap::types::Flag::Seen => Some(Self::Seen),
|
||||
imap::types::Flag::Answered => Some(Self::Answered),
|
||||
imap::types::Flag::Flagged => Some(Self::Flagged),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
// impl Flag {
|
||||
// fn from_imap_flag(flag: &imap::types::Flag<'_>) -> Option<Self> {
|
||||
// match flag {
|
||||
// imap::types::Flag::Seen => Some(Self::Seen),
|
||||
// imap::types::Flag::Answered => Some(Self::Answered),
|
||||
// imap::types::Flag::Flagged => Some(Self::Flagged),
|
||||
// _ => None,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Msg {
|
||||
pub struct Msg<'m> {
|
||||
pub uid: u32,
|
||||
pub flags: Vec<Flag>,
|
||||
pub flags: Flags<'m>,
|
||||
pub subject: String,
|
||||
pub sender: String,
|
||||
pub date: String,
|
||||
|
@ -201,11 +202,11 @@ pub struct Msg {
|
|||
raw: Vec<u8>,
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Msg {
|
||||
impl<'m> From<Vec<u8>> for Msg<'m> {
|
||||
fn from(raw: Vec<u8>) -> Self {
|
||||
Self {
|
||||
uid: 0,
|
||||
flags: vec![],
|
||||
flags: Flags::new(&[]),
|
||||
subject: String::from(""),
|
||||
sender: String::from(""),
|
||||
date: String::from(""),
|
||||
|
@ -214,23 +215,19 @@ impl From<Vec<u8>> for Msg {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<String> for Msg {
|
||||
impl<'m> From<String> for Msg<'m> {
|
||||
fn from(raw: String) -> Self {
|
||||
Self::from(raw.as_bytes().to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&imap::types::Fetch> for Msg {
|
||||
fn from(fetch: &imap::types::Fetch) -> Self {
|
||||
impl<'m> From<&'m imap::types::Fetch> for Msg<'m> {
|
||||
fn from(fetch: &'m imap::types::Fetch) -> Self {
|
||||
match fetch.envelope() {
|
||||
None => Self::from(fetch.body().unwrap_or_default().to_vec()),
|
||||
Some(envelope) => Self {
|
||||
uid: fetch.uid.unwrap_or_default(),
|
||||
flags: fetch
|
||||
.flags()
|
||||
.into_iter()
|
||||
.filter_map(Flag::from_imap_flag)
|
||||
.collect::<Vec<_>>(),
|
||||
flags: Flags::new(fetch.flags()),
|
||||
subject: envelope
|
||||
.subject
|
||||
.and_then(|subj| rfc2047_decoder::decode(subj).ok())
|
||||
|
@ -251,32 +248,8 @@ impl From<&imap::types::Fetch> for Msg {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Msg {
|
||||
pub fn display_flags(&self) -> String {
|
||||
let mut flags = String::new();
|
||||
|
||||
flags.push_str(if self.flags.contains(&Flag::Seen) {
|
||||
" "
|
||||
} else {
|
||||
"✶"
|
||||
});
|
||||
|
||||
flags.push_str(if self.flags.contains(&Flag::Answered) {
|
||||
"↩"
|
||||
} else {
|
||||
" "
|
||||
});
|
||||
|
||||
flags.push_str(if self.flags.contains(&Flag::Flagged) {
|
||||
"!"
|
||||
} else {
|
||||
" "
|
||||
});
|
||||
|
||||
flags
|
||||
}
|
||||
|
||||
pub fn parse(&'a self) -> Result<mailparse::ParsedMail<'a>> {
|
||||
impl<'m> Msg<'m> {
|
||||
pub fn parse(&'m self) -> Result<mailparse::ParsedMail<'m>> {
|
||||
Ok(mailparse::parse_mail(&self.raw)?)
|
||||
}
|
||||
|
||||
|
@ -549,7 +522,7 @@ impl<'a> Msg {
|
|||
}
|
||||
}
|
||||
|
||||
impl DisplayRow for Msg {
|
||||
impl<'m> DisplayRow for Msg<'m> {
|
||||
fn to_row(&self) -> Vec<table::Cell> {
|
||||
use crate::table::*;
|
||||
|
||||
|
@ -561,7 +534,7 @@ impl DisplayRow for Msg {
|
|||
|
||||
vec![
|
||||
Cell::new(&[unseen.to_owned(), RED], &self.uid.to_string()),
|
||||
Cell::new(&[unseen.to_owned(), WHITE], &self.display_flags()),
|
||||
Cell::new(&[unseen.to_owned(), WHITE], &self.flags.to_string()),
|
||||
FlexCell::new(&[unseen.to_owned(), GREEN], &self.subject),
|
||||
Cell::new(&[unseen.to_owned(), BLUE], &self.sender),
|
||||
Cell::new(&[unseen.to_owned(), YELLOW], &self.date),
|
||||
|
@ -572,9 +545,9 @@ impl DisplayRow for Msg {
|
|||
// Msgs
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Msgs(pub Vec<Msg>);
|
||||
pub struct Msgs<'m>(pub Vec<Msg<'m>>);
|
||||
|
||||
impl<'a> DisplayTable<'a, Msg> for Msgs {
|
||||
impl<'m> DisplayTable<'m, Msg<'m>> for Msgs<'m> {
|
||||
fn header_row() -> Vec<table::Cell> {
|
||||
use crate::table::*;
|
||||
|
||||
|
@ -587,12 +560,18 @@ impl<'a> DisplayTable<'a, Msg> for Msgs {
|
|||
]
|
||||
}
|
||||
|
||||
fn rows(&self) -> &Vec<Msg> {
|
||||
fn rows(&self) -> &Vec<Msg<'m>> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Msgs {
|
||||
impl<'m> From<&'m imap::types::ZeroCopy<Vec<imap::types::Fetch>>> for Msgs<'m> {
|
||||
fn from(fetches: &'m imap::types::ZeroCopy<Vec<imap::types::Fetch>>) -> Self {
|
||||
Self(fetches.iter().map(Msg::from).collect::<Vec<_>>())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'m> fmt::Display for Msgs<'m> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "\n{}", self.to_table())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue