improve list msgs perf, remove flags column

This commit is contained in:
Clément DOUIN 2021-01-18 11:57:53 +01:00
parent d392d71129
commit 5e948b5cc1
No known key found for this signature in database
GPG key ID: 69C9B9CFFDEE2DEF
8 changed files with 82 additions and 76 deletions

12
Cargo.lock generated
View file

@ -235,6 +235,7 @@ dependencies = [
"lettre",
"mailparse",
"native-tls",
"rfc2047-decoder",
"serde",
"serde_json",
"terminal_size",
@ -707,6 +708,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "rfc2047-decoder"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87ecf2ba387f446155e26796aabb727e9ae1427dd13ac9cc21773a3fbda19d77"
dependencies = [
"base64 0.13.0",
"charset",
"quoted_printable",
]
[[package]]
name = "ryu"
version = "1.0.5"

View file

@ -11,6 +11,7 @@ imap = "2.4.0"
lettre = "0.10.0-alpha.4"
mailparse = "0.13.1"
native-tls = "0.2"
rfc2047-decoder = "0.1.2"
serde = { version = "1.0.118", features = ["derive"] }
serde_json = "1.0.61"
terminal_size = "0.1.15"

View file

@ -167,7 +167,7 @@ impl Config {
Ok(toml::from_slice(&content)?)
}
pub fn get_account(&self, name: Option<&str>) -> Result<&Account> {
pub fn find_account_by_name(&self, name: Option<&str>) -> Result<&Account> {
match name {
Some(name) => self
.accounts

View file

@ -88,8 +88,8 @@ impl<'a> ImapConnector<'a> {
Ok(Self { account, sess })
}
pub fn close(&mut self) {
match self.sess.close() {
pub fn logout(&mut self) {
match self.sess.logout() {
_ => (),
}
}
@ -113,7 +113,7 @@ impl<'a> ImapConnector<'a> {
let msgs = self
.sess
.fetch(range, "(UID BODY.PEEK[])")?
.fetch(range, "(UID ENVELOPE INTERNALDATE)")?
.iter()
.rev()
.map(Msg::from)
@ -143,9 +143,8 @@ impl<'a> ImapConnector<'a> {
let msgs = self
.sess
.fetch(range, "(UID BODY.PEEK[])")?
.fetch(range, "(UID ENVELOPE INTERNALDATE)")?
.iter()
.rev()
.map(Msg::from)
.collect::<Vec<_>>();

View file

@ -221,19 +221,20 @@ fn run() -> Result<()> {
if let Some(_) = matches.subcommand_matches("mailboxes") {
let config = Config::new_from_file()?;
let account = config.get_account(account_name)?;
let account = config.find_account_by_name(account_name)?;
let mut imap_conn = ImapConnector::new(&account)?;
let mboxes = imap_conn.list_mboxes()?;
print(&output_type, mboxes)?;
imap_conn.close();
imap_conn.logout();
}
if let Some(matches) = matches.subcommand_matches("list") {
let config = Config::new_from_file()?;
let account = config.get_account(account_name)?;
let account = config.find_account_by_name(account_name)?;
let mut imap_conn = ImapConnector::new(&account)?;
let mbox = matches.value_of("mailbox").unwrap();
let page_size: u32 = matches
.value_of("size")
@ -249,12 +250,12 @@ fn run() -> Result<()> {
let msgs = imap_conn.list_msgs(&mbox, &page_size, &page)?;
print(&output_type, msgs)?;
imap_conn.close();
imap_conn.logout();
}
if let Some(matches) = matches.subcommand_matches("search") {
let config = Config::new_from_file()?;
let account = config.get_account(account_name)?;
let account = config.find_account_by_name(account_name)?;
let mut imap_conn = ImapConnector::new(&account)?;
let mbox = matches.value_of("mailbox").unwrap();
let page_size: usize = matches
@ -295,12 +296,12 @@ fn run() -> Result<()> {
let msgs = imap_conn.search_msgs(&mbox, &query, &page_size, &page)?;
print(&output_type, msgs)?;
imap_conn.close();
imap_conn.logout();
}
if let Some(matches) = matches.subcommand_matches("read") {
let config = Config::new_from_file()?;
let account = config.get_account(account_name)?;
let account = config.find_account_by_name(account_name)?;
let mut imap_conn = ImapConnector::new(&account)?;
let mbox = matches.value_of("mailbox").unwrap();
let uid = matches.value_of("uid").unwrap();
@ -310,12 +311,12 @@ fn run() -> Result<()> {
let text_bodies = msg.text_bodies(&mime)?;
println!("{}", text_bodies);
imap_conn.close();
imap_conn.logout();
}
if let Some(matches) = matches.subcommand_matches("attachments") {
let config = Config::new_from_file()?;
let account = config.get_account(account_name)?;
let account = config.find_account_by_name(account_name)?;
let mut imap_conn = ImapConnector::new(&account)?;
let mbox = matches.value_of("mailbox").unwrap();
let uid = matches.value_of("uid").unwrap();
@ -334,12 +335,12 @@ fn run() -> Result<()> {
println!("Done!");
}
imap_conn.close();
imap_conn.logout();
}
if let Some(_) = matches.subcommand_matches("write") {
let config = Config::new_from_file()?;
let account = config.get_account(account_name)?;
let account = config.find_account_by_name(account_name)?;
let mut imap_conn = ImapConnector::new(&account)?;
let tpl = Msg::build_new_tpl(&config, &account)?;
let content = input::open_editor_with_tpl(&tpl.as_bytes())?;
@ -352,12 +353,12 @@ fn run() -> Result<()> {
imap_conn.append_msg("Sent", &msg.to_vec()?)?;
println!("Done!");
imap_conn.close();
imap_conn.logout();
}
if let Some(matches) = matches.subcommand_matches("reply") {
let config = Config::new_from_file()?;
let account = config.get_account(account_name)?;
let account = config.find_account_by_name(account_name)?;
let mut imap_conn = ImapConnector::new(&account)?;
let mbox = matches.value_of("mailbox").unwrap();
@ -380,12 +381,12 @@ fn run() -> Result<()> {
imap_conn.append_msg("Sent", &msg.to_vec()?)?;
println!("Done!");
imap_conn.close();
imap_conn.logout();
}
if let Some(matches) = matches.subcommand_matches("forward") {
let config = Config::new_from_file()?;
let account = config.get_account(account_name)?;
let account = config.find_account_by_name(account_name)?;
let mut imap_conn = ImapConnector::new(&account)?;
let mbox = matches.value_of("mailbox").unwrap();
@ -403,7 +404,7 @@ fn run() -> Result<()> {
imap_conn.append_msg("Sent", &msg.to_vec()?)?;
println!("Done!");
imap_conn.close();
imap_conn.logout();
}
Ok(())

View file

@ -1,5 +1,6 @@
use lettre;
use mailparse::{self, MailHeaderMap};
use rfc2047_decoder;
use serde::Serialize;
use std::{fmt, result};
@ -45,38 +46,54 @@ type Result<T> = result::Result<T, Error>;
#[derive(Debug, Serialize)]
pub struct Msg {
pub uid: u32,
pub flags: Vec<String>,
pub subject: String,
pub sender: String,
pub date: String,
#[serde(skip_serializing)]
raw: Vec<u8>,
}
impl From<String> for Msg {
fn from(item: String) -> Self {
impl From<Vec<u8>> for Msg {
fn from(raw: Vec<u8>) -> Self {
Self {
uid: 0,
flags: vec![],
raw: item.as_bytes().to_vec(),
subject: String::from(""),
sender: String::from(""),
date: String::from(""),
raw,
}
}
}
impl From<Vec<u8>> for Msg {
fn from(item: Vec<u8>) -> Self {
Self {
uid: 0,
flags: vec![],
raw: item,
}
impl From<String> for Msg {
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 {
Self {
uid: fetch.uid.unwrap_or_default(),
flags: vec![],
raw: fetch.body().unwrap_or_default().to_vec(),
match fetch.envelope() {
None => Self::from(fetch.body().unwrap_or_default().to_vec()),
Some(envelope) => Self {
uid: fetch.uid.unwrap_or_default(),
subject: envelope
.subject
.and_then(|subj| rfc2047_decoder::decode(subj).ok())
.unwrap_or_default(),
sender: envelope
.from
.as_ref()
.and_then(|addrs| addrs.first()?.name)
.and_then(|name| rfc2047_decoder::decode(name).ok())
.unwrap_or_default(),
date: fetch
.internal_date()
.map(|date| date.naive_local().to_string())
.unwrap_or_default(),
raw: fetch.body().unwrap_or_default().to_vec(),
},
}
}
}
@ -376,36 +393,14 @@ impl<'a> Msg {
impl DisplayRow for Msg {
fn to_row(&self) -> Vec<table::Cell> {
match self.parse() {
Err(_) => vec![],
Ok(parsed) => {
let headers = parsed.get_headers();
use crate::table::*;
let uid = &self.uid.to_string();
let flags = match self.extract_attachments().map(|vec| vec.is_empty()) {
Ok(false) => "",
_ => " ",
};
let sender = headers
.get_first_value("reply-to")
.or(headers.get_first_value("from"))
.unwrap_or_default();
let subject = headers.get_first_value("subject").unwrap_or_default();
let date = headers.get_first_value("date").unwrap_or_default();
{
use crate::table::*;
vec![
Cell::new(&[RED], &uid),
Cell::new(&[WHITE], &flags),
Cell::new(&[BLUE], &sender),
FlexCell::new(&[GREEN], &subject),
Cell::new(&[YELLOW], &date),
]
}
}
}
vec![
Cell::new(&[RED], &self.uid.to_string()),
Cell::new(&[BLUE], &self.sender),
FlexCell::new(&[GREEN], &self.subject),
Cell::new(&[YELLOW], &self.date),
]
}
}
@ -420,7 +415,6 @@ impl<'a> DisplayTable<'a, Msg> for Msgs {
vec![
Cell::new(&[BOLD, UNDERLINE, WHITE], "UID"),
Cell::new(&[BOLD, UNDERLINE, WHITE], "FLAGS"),
Cell::new(&[BOLD, UNDERLINE, WHITE], "SENDER"),
FlexCell::new(&[BOLD, UNDERLINE, WHITE], "SUBJECT"),
Cell::new(&[BOLD, UNDERLINE, WHITE], "DATE"),

View file

@ -42,6 +42,9 @@ type Result<T> = result::Result<T, Error>;
pub fn send(account: &Account, msg: &lettre::Message) -> Result<()> {
use lettre::Transport;
// TODO
// lettre::transport::smtp::SmtpTransport::starttls_relay
lettre::transport::smtp::SmtpTransport::relay(&account.smtp_host)?
.credentials(account.smtp_creds()?)
.build()

View file

@ -60,15 +60,11 @@ impl Cell {
let style_end = "\x1b[0m";
if col_size > 0 && self.printable_value_len() > col_size {
let col_size = self
.value
.char_indices()
.map(|(i, _)| i)
.nth(col_size)
.unwrap()
- 2;
let value: String = self.value.chars().collect::<Vec<_>>()[0..=col_size - 2]
.into_iter()
.collect();
String::from(style_begin + &self.value[0..=col_size] + "" + style_end)
String::from(style_begin + &value + "" + style_end)
} else {
let padding = if col_size == 0 {
"".to_string()