mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-21 18:40:19 +00:00
improve list msgs perf, remove flags column
This commit is contained in:
parent
d392d71129
commit
5e948b5cc1
8 changed files with 82 additions and 76 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<_>>();
|
||||
|
||||
|
|
33
src/main.rs
33
src/main.rs
|
@ -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(())
|
||||
|
|
86
src/msg.rs
86
src/msg.rs
|
@ -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"),
|
||||
|
|
|
@ -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()
|
||||
|
|
12
src/table.rs
12
src/table.rs
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue