mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-08 18:45:13 +00:00
improve list msgs perf, remove flags column
This commit is contained in:
parent
d392d71129
commit
5e948b5cc1
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -235,6 +235,7 @@ dependencies = [
|
||||||
"lettre",
|
"lettre",
|
||||||
"mailparse",
|
"mailparse",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
|
"rfc2047-decoder",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"terminal_size",
|
"terminal_size",
|
||||||
|
@ -707,6 +708,17 @@ dependencies = [
|
||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
|
|
|
@ -11,6 +11,7 @@ imap = "2.4.0"
|
||||||
lettre = "0.10.0-alpha.4"
|
lettre = "0.10.0-alpha.4"
|
||||||
mailparse = "0.13.1"
|
mailparse = "0.13.1"
|
||||||
native-tls = "0.2"
|
native-tls = "0.2"
|
||||||
|
rfc2047-decoder = "0.1.2"
|
||||||
serde = { version = "1.0.118", features = ["derive"] }
|
serde = { version = "1.0.118", features = ["derive"] }
|
||||||
serde_json = "1.0.61"
|
serde_json = "1.0.61"
|
||||||
terminal_size = "0.1.15"
|
terminal_size = "0.1.15"
|
||||||
|
|
|
@ -167,7 +167,7 @@ impl Config {
|
||||||
Ok(toml::from_slice(&content)?)
|
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 {
|
match name {
|
||||||
Some(name) => self
|
Some(name) => self
|
||||||
.accounts
|
.accounts
|
||||||
|
|
|
@ -88,8 +88,8 @@ impl<'a> ImapConnector<'a> {
|
||||||
Ok(Self { account, sess })
|
Ok(Self { account, sess })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(&mut self) {
|
pub fn logout(&mut self) {
|
||||||
match self.sess.close() {
|
match self.sess.logout() {
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ impl<'a> ImapConnector<'a> {
|
||||||
|
|
||||||
let msgs = self
|
let msgs = self
|
||||||
.sess
|
.sess
|
||||||
.fetch(range, "(UID BODY.PEEK[])")?
|
.fetch(range, "(UID ENVELOPE INTERNALDATE)")?
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.map(Msg::from)
|
.map(Msg::from)
|
||||||
|
@ -143,9 +143,8 @@ impl<'a> ImapConnector<'a> {
|
||||||
|
|
||||||
let msgs = self
|
let msgs = self
|
||||||
.sess
|
.sess
|
||||||
.fetch(range, "(UID BODY.PEEK[])")?
|
.fetch(range, "(UID ENVELOPE INTERNALDATE)")?
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
|
||||||
.map(Msg::from)
|
.map(Msg::from)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
|
33
src/main.rs
33
src/main.rs
|
@ -221,19 +221,20 @@ fn run() -> Result<()> {
|
||||||
|
|
||||||
if let Some(_) = matches.subcommand_matches("mailboxes") {
|
if let Some(_) = matches.subcommand_matches("mailboxes") {
|
||||||
let config = Config::new_from_file()?;
|
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 mut imap_conn = ImapConnector::new(&account)?;
|
||||||
|
|
||||||
let mboxes = imap_conn.list_mboxes()?;
|
let mboxes = imap_conn.list_mboxes()?;
|
||||||
print(&output_type, mboxes)?;
|
print(&output_type, mboxes)?;
|
||||||
|
|
||||||
imap_conn.close();
|
imap_conn.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("list") {
|
if let Some(matches) = matches.subcommand_matches("list") {
|
||||||
let config = Config::new_from_file()?;
|
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 mut imap_conn = ImapConnector::new(&account)?;
|
||||||
|
|
||||||
let mbox = matches.value_of("mailbox").unwrap();
|
let mbox = matches.value_of("mailbox").unwrap();
|
||||||
let page_size: u32 = matches
|
let page_size: u32 = matches
|
||||||
.value_of("size")
|
.value_of("size")
|
||||||
|
@ -249,12 +250,12 @@ fn run() -> Result<()> {
|
||||||
let msgs = imap_conn.list_msgs(&mbox, &page_size, &page)?;
|
let msgs = imap_conn.list_msgs(&mbox, &page_size, &page)?;
|
||||||
print(&output_type, msgs)?;
|
print(&output_type, msgs)?;
|
||||||
|
|
||||||
imap_conn.close();
|
imap_conn.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("search") {
|
if let Some(matches) = matches.subcommand_matches("search") {
|
||||||
let config = Config::new_from_file()?;
|
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 mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let mbox = matches.value_of("mailbox").unwrap();
|
let mbox = matches.value_of("mailbox").unwrap();
|
||||||
let page_size: usize = matches
|
let page_size: usize = matches
|
||||||
|
@ -295,12 +296,12 @@ fn run() -> Result<()> {
|
||||||
let msgs = imap_conn.search_msgs(&mbox, &query, &page_size, &page)?;
|
let msgs = imap_conn.search_msgs(&mbox, &query, &page_size, &page)?;
|
||||||
print(&output_type, msgs)?;
|
print(&output_type, msgs)?;
|
||||||
|
|
||||||
imap_conn.close();
|
imap_conn.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("read") {
|
if let Some(matches) = matches.subcommand_matches("read") {
|
||||||
let config = Config::new_from_file()?;
|
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 mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let mbox = matches.value_of("mailbox").unwrap();
|
let mbox = matches.value_of("mailbox").unwrap();
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
|
@ -310,12 +311,12 @@ fn run() -> Result<()> {
|
||||||
let text_bodies = msg.text_bodies(&mime)?;
|
let text_bodies = msg.text_bodies(&mime)?;
|
||||||
println!("{}", text_bodies);
|
println!("{}", text_bodies);
|
||||||
|
|
||||||
imap_conn.close();
|
imap_conn.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("attachments") {
|
if let Some(matches) = matches.subcommand_matches("attachments") {
|
||||||
let config = Config::new_from_file()?;
|
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 mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let mbox = matches.value_of("mailbox").unwrap();
|
let mbox = matches.value_of("mailbox").unwrap();
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
|
@ -334,12 +335,12 @@ fn run() -> Result<()> {
|
||||||
println!("Done!");
|
println!("Done!");
|
||||||
}
|
}
|
||||||
|
|
||||||
imap_conn.close();
|
imap_conn.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_) = matches.subcommand_matches("write") {
|
if let Some(_) = matches.subcommand_matches("write") {
|
||||||
let config = Config::new_from_file()?;
|
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 mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let tpl = Msg::build_new_tpl(&config, &account)?;
|
let tpl = Msg::build_new_tpl(&config, &account)?;
|
||||||
let content = input::open_editor_with_tpl(&tpl.as_bytes())?;
|
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()?)?;
|
imap_conn.append_msg("Sent", &msg.to_vec()?)?;
|
||||||
println!("Done!");
|
println!("Done!");
|
||||||
|
|
||||||
imap_conn.close();
|
imap_conn.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("reply") {
|
if let Some(matches) = matches.subcommand_matches("reply") {
|
||||||
let config = Config::new_from_file()?;
|
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 mut imap_conn = ImapConnector::new(&account)?;
|
||||||
|
|
||||||
let mbox = matches.value_of("mailbox").unwrap();
|
let mbox = matches.value_of("mailbox").unwrap();
|
||||||
|
@ -380,12 +381,12 @@ fn run() -> Result<()> {
|
||||||
imap_conn.append_msg("Sent", &msg.to_vec()?)?;
|
imap_conn.append_msg("Sent", &msg.to_vec()?)?;
|
||||||
println!("Done!");
|
println!("Done!");
|
||||||
|
|
||||||
imap_conn.close();
|
imap_conn.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("forward") {
|
if let Some(matches) = matches.subcommand_matches("forward") {
|
||||||
let config = Config::new_from_file()?;
|
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 mut imap_conn = ImapConnector::new(&account)?;
|
||||||
|
|
||||||
let mbox = matches.value_of("mailbox").unwrap();
|
let mbox = matches.value_of("mailbox").unwrap();
|
||||||
|
@ -403,7 +404,7 @@ fn run() -> Result<()> {
|
||||||
imap_conn.append_msg("Sent", &msg.to_vec()?)?;
|
imap_conn.append_msg("Sent", &msg.to_vec()?)?;
|
||||||
println!("Done!");
|
println!("Done!");
|
||||||
|
|
||||||
imap_conn.close();
|
imap_conn.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
86
src/msg.rs
86
src/msg.rs
|
@ -1,5 +1,6 @@
|
||||||
use lettre;
|
use lettre;
|
||||||
use mailparse::{self, MailHeaderMap};
|
use mailparse::{self, MailHeaderMap};
|
||||||
|
use rfc2047_decoder;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::{fmt, result};
|
use std::{fmt, result};
|
||||||
|
|
||||||
|
@ -45,38 +46,54 @@ type Result<T> = result::Result<T, Error>;
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct Msg {
|
pub struct Msg {
|
||||||
pub uid: u32,
|
pub uid: u32,
|
||||||
pub flags: Vec<String>,
|
pub subject: String,
|
||||||
|
pub sender: String,
|
||||||
|
pub date: String,
|
||||||
|
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
raw: Vec<u8>,
|
raw: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for Msg {
|
impl From<Vec<u8>> for Msg {
|
||||||
fn from(item: String) -> Self {
|
fn from(raw: Vec<u8>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
uid: 0,
|
uid: 0,
|
||||||
flags: vec![],
|
subject: String::from(""),
|
||||||
raw: item.as_bytes().to_vec(),
|
sender: String::from(""),
|
||||||
|
date: String::from(""),
|
||||||
|
raw,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<u8>> for Msg {
|
impl From<String> for Msg {
|
||||||
fn from(item: Vec<u8>) -> Self {
|
fn from(raw: String) -> Self {
|
||||||
Self {
|
Self::from(raw.as_bytes().to_vec())
|
||||||
uid: 0,
|
|
||||||
flags: vec![],
|
|
||||||
raw: item,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&imap::types::Fetch> for Msg {
|
impl From<&imap::types::Fetch> for Msg {
|
||||||
fn from(fetch: &imap::types::Fetch) -> Self {
|
fn from(fetch: &imap::types::Fetch) -> Self {
|
||||||
Self {
|
match fetch.envelope() {
|
||||||
uid: fetch.uid.unwrap_or_default(),
|
None => Self::from(fetch.body().unwrap_or_default().to_vec()),
|
||||||
flags: vec![],
|
Some(envelope) => Self {
|
||||||
raw: fetch.body().unwrap_or_default().to_vec(),
|
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 {
|
impl DisplayRow for Msg {
|
||||||
fn to_row(&self) -> Vec<table::Cell> {
|
fn to_row(&self) -> Vec<table::Cell> {
|
||||||
match self.parse() {
|
use crate::table::*;
|
||||||
Err(_) => vec![],
|
|
||||||
Ok(parsed) => {
|
|
||||||
let headers = parsed.get_headers();
|
|
||||||
|
|
||||||
let uid = &self.uid.to_string();
|
vec![
|
||||||
let flags = match self.extract_attachments().map(|vec| vec.is_empty()) {
|
Cell::new(&[RED], &self.uid.to_string()),
|
||||||
Ok(false) => "",
|
Cell::new(&[BLUE], &self.sender),
|
||||||
_ => " ",
|
FlexCell::new(&[GREEN], &self.subject),
|
||||||
};
|
Cell::new(&[YELLOW], &self.date),
|
||||||
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),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,7 +415,6 @@ impl<'a> DisplayTable<'a, Msg> for Msgs {
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
Cell::new(&[BOLD, UNDERLINE, WHITE], "UID"),
|
Cell::new(&[BOLD, UNDERLINE, WHITE], "UID"),
|
||||||
Cell::new(&[BOLD, UNDERLINE, WHITE], "FLAGS"),
|
|
||||||
Cell::new(&[BOLD, UNDERLINE, WHITE], "SENDER"),
|
Cell::new(&[BOLD, UNDERLINE, WHITE], "SENDER"),
|
||||||
FlexCell::new(&[BOLD, UNDERLINE, WHITE], "SUBJECT"),
|
FlexCell::new(&[BOLD, UNDERLINE, WHITE], "SUBJECT"),
|
||||||
Cell::new(&[BOLD, UNDERLINE, WHITE], "DATE"),
|
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<()> {
|
pub fn send(account: &Account, msg: &lettre::Message) -> Result<()> {
|
||||||
use lettre::Transport;
|
use lettre::Transport;
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// lettre::transport::smtp::SmtpTransport::starttls_relay
|
||||||
|
|
||||||
lettre::transport::smtp::SmtpTransport::relay(&account.smtp_host)?
|
lettre::transport::smtp::SmtpTransport::relay(&account.smtp_host)?
|
||||||
.credentials(account.smtp_creds()?)
|
.credentials(account.smtp_creds()?)
|
||||||
.build()
|
.build()
|
||||||
|
|
12
src/table.rs
12
src/table.rs
|
@ -60,15 +60,11 @@ impl Cell {
|
||||||
let style_end = "\x1b[0m";
|
let style_end = "\x1b[0m";
|
||||||
|
|
||||||
if col_size > 0 && self.printable_value_len() > col_size {
|
if col_size > 0 && self.printable_value_len() > col_size {
|
||||||
let col_size = self
|
let value: String = self.value.chars().collect::<Vec<_>>()[0..=col_size - 2]
|
||||||
.value
|
.into_iter()
|
||||||
.char_indices()
|
.collect();
|
||||||
.map(|(i, _)| i)
|
|
||||||
.nth(col_size)
|
|
||||||
.unwrap()
|
|
||||||
- 2;
|
|
||||||
|
|
||||||
String::from(style_begin + &self.value[0..=col_size] + "… " + style_end)
|
String::from(style_begin + &value + "… " + style_end)
|
||||||
} else {
|
} else {
|
||||||
let padding = if col_size == 0 {
|
let padding = if col_size == 0 {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
|
|
Loading…
Reference in a new issue