browse: show mail author and timestamp

This commit is contained in:
FliegendeWurst 2021-04-05 20:48:37 +02:00 committed by Arne Keller
parent a171abfdae
commit 7d2e6de827
6 changed files with 125 additions and 40 deletions

40
Cargo.lock generated
View File

@ -213,7 +213,7 @@ dependencies = [
"log",
"num",
"owning_ref",
"syn 1.0.67",
"syn 1.0.68",
"unicode-segmentation",
"unicode-width",
"wasmer_enumset",
@ -223,11 +223,11 @@ dependencies = [
[[package]]
name = "cursive_tree_view"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd59affc6d600a69df27972fb5fe7f38a743aeb0710299e3324022c7d80717a0"
source = "git+https://github.com/FliegendeWurst/cursive_tree_view.git?branch=master#f3a0470f229a5ab57da600cf552b916ad33c6390"
dependencies = [
"cursive_core",
"debug_stub_derive",
"unicode-segmentation",
]
[[package]]
@ -251,7 +251,7 @@ dependencies = [
"proc-macro2",
"quote 1.0.9",
"strsim",
"syn 1.0.67",
"syn 1.0.68",
]
[[package]]
@ -262,7 +262,7 @@ checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
dependencies = [
"darling_core",
"quote 1.0.9",
"syn 1.0.67",
"syn 1.0.68",
]
[[package]]
@ -335,7 +335,7 @@ checksum = "e5c450cf304c9e18d45db562025a14fb1ca0f5c769b6f609309f81d4c31de455"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.67",
"syn 1.0.68",
]
[[package]]
@ -525,8 +525,8 @@ checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714"
[[package]]
name = "libsqlite3-sys"
version = "0.21.0"
source = "git+https://github.com/rusqlite/rusqlite?branch=master#ed3bfbdf9d9e577e8d4cff937b053a0e429a4cd7"
version = "0.22.0"
source = "git+https://github.com/rusqlite/rusqlite?branch=master#ddf69f749a67fdb673837ebb3c881a522fd7f638"
dependencies = [
"cc",
"pkg-config",
@ -595,9 +595,9 @@ dependencies = [
[[package]]
name = "mime2ext"
version = "0.1.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b88947611258697e12f8602a44003b0885ca5fe30f27132d63c8f47fe98f2f2e"
checksum = "d8b337a0b7c1d5d8d3c08096823831a5d7a3f6aadea1150b7fe622c38c14e283"
dependencies = [
"serde",
"serde_json",
@ -751,9 +751,9 @@ checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
[[package]]
name = "proc-macro2"
version = "1.0.24"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
dependencies = [
"unicode-xid 0.2.1",
]
@ -851,8 +851,8 @@ dependencies = [
[[package]]
name = "rusqlite"
version = "0.24.2"
source = "git+https://github.com/rusqlite/rusqlite?branch=master#ed3bfbdf9d9e577e8d4cff937b053a0e429a4cd7"
version = "0.25.0"
source = "git+https://github.com/rusqlite/rusqlite?branch=master#ddf69f749a67fdb673837ebb3c881a522fd7f638"
dependencies = [
"bitflags",
"fallible-iterator",
@ -995,7 +995,7 @@ checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.67",
"syn 1.0.68",
]
[[package]]
@ -1081,9 +1081,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.67"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6498a9efc342871f91cc2d0d694c674368b4ceb40f62b65a7a08c3792935e702"
checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87"
dependencies = [
"proc-macro2",
"quote 1.0.9",
@ -1206,7 +1206,7 @@ dependencies = [
"log",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.67",
"syn 1.0.68",
"wasm-bindgen-shared",
]
@ -1228,7 +1228,7 @@ checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.67",
"syn 1.0.68",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -1258,7 +1258,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.67",
"syn 1.0.68",
]
[[package]]

View File

@ -25,4 +25,7 @@ subprocess = "0.2.6"
mime2ext = "0.1.2"
petgraph = "0.5.1"
cursive = { version = "0.16.3", default-features = false, features = ["termion-backend"] }
cursive_tree_view = "0.7.0"
cursive_tree_view = { git = "https://github.com/FliegendeWurst/cursive_tree_view.git", branch = "master" }
[profile.release]
overflow-checks = true

View File

@ -6,7 +6,7 @@ use cursive::{Cursive, CursiveExt};
use cursive::traits::Identifiable;
use cursive::view::{Scrollable, SizeConstraint};
use cursive::views::{LinearLayout, ResizedView, TextView};
use cursive_tree_view::{Placement, TreeView};
use cursive_tree_view::{Placement, TreeEntry, TreeView};
use inboxid::*;
use io::Write;
use itertools::Itertools;
@ -25,8 +25,8 @@ fn main() -> Result<()> {
}
});
match result {
Ok(res) => res,
Err(_) => {
Ok(res) => res,
Err(_) => {
if let Err(e) = io::stderr().lock().write_all(&sink.lock().unwrap()) {
println!("{:?}", e);
}
@ -62,7 +62,7 @@ fn show_listing(mailbox: &str) -> Result<()> {
} else {
flags_display.push('*');
}
rows.push(IntoIter::new([(mails.len() - i).to_string(), flags_display, mail.from.clone(), mail.subject.clone(), mail.date_iso.clone()]));
rows.push(IntoIter::new([(mails.len() - i).to_string(), flags_display, mail.from(), mail.subject.clone(), mail.date_iso.clone()]));
}
let mut mails_by_id = HashMap::new();
@ -251,15 +251,17 @@ struct MailPart {
}
impl Display for MailPart {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.part.ctype.mimetype)
}
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.part.ctype.mimetype)
}
}
impl From<&'static ParsedMail<'static>> for MailPart {
fn from(part: &'static ParsedMail<'static>) -> Self {
Self {
fn from(part: &'static ParsedMail<'static>) -> Self {
Self {
part
}
}
}
}
impl TreeEntry for MailPart {}

View File

@ -40,7 +40,7 @@ fn show_listing(mailbox: &str) -> Result<()> {
} else {
flags_display.push('*');
}
rows.push(IntoIter::new([(mails.len() - i).to_string(), flags_display, mail.from.clone(), mail.subject.clone(), mail.date_iso.clone()]));
rows.push(IntoIter::new([(mails.len() - i).to_string(), flags_display, mail.from(), mail.subject.clone(), mail.date_iso.clone()]));
}
let mut ascii_table = AsciiTable::default();

View File

@ -26,7 +26,7 @@ fn show_listing(mailbox: &str) -> Result<()> {
let mut rows = Vec::new();
for mail in &mails {
rows.push(IntoIter::new([mail.from.clone(), mail.subject.clone(), mail.date_iso.clone()]));
rows.push(IntoIter::new([mail.from(), mail.subject.clone(), mail.date_iso.clone()]));
}
let mut ascii_table = AsciiTable::default();

View File

@ -1,10 +1,12 @@
use std::{borrow::Cow, convert::{TryFrom, TryInto}, env, fmt::{Debug, Display}, fs, hash::Hash, io, net::TcpStream, ops::Deref};
use std::{borrow::Cow, cmp, convert::{TryFrom, TryInto}, env, fmt::{Debug, Display}, fs, hash::Hash, io, net::TcpStream, ops::Deref};
use anyhow::Context;
use chrono::{DateTime, Local, NaiveDateTime, TimeZone};
use cursive::{theme::Style, utils::span::{IndexedCow, IndexedSpan, SpannedString}};
use cursive_tree_view::TreeEntry;
use imap::{Session, types::Flag};
use maildir::{MailEntry, Maildir};
use mailparse::{MailHeaderMap, ParsedMail, dateparse};
use mailparse::{MailHeaderMap, ParsedMail, SingleInfo, addrparse, dateparse};
use petgraph::{Graph, graph::NodeIndex};
use rusqlite::{Connection, params};
use rustls_connector::{RustlsConnector, rustls::{ClientSession, StreamOwned}};
@ -96,7 +98,7 @@ pub struct EasyMail<'a> {
mail: Option<ParsedMail<'a>>,
pub id: MaildirID,
pub flags: String,
pub from: String,
from: SingleInfo,
pub subject: String,
pub date: DateTime<Local>,
pub date_iso: String,
@ -108,7 +110,10 @@ impl EasyMail<'_> {
mail: None,
id: MaildirID::new(0, 0),
flags: "S".to_owned(),
from: String::new(),
from: SingleInfo {
display_name: None,
addr: String::new()
},
subject,
date: Local.from_utc_datetime(&NaiveDateTime::from_timestamp(0, 0)),
date_iso: "????-??-??".to_owned()
@ -119,6 +124,10 @@ impl EasyMail<'_> {
self.mail.is_none()
}
pub fn from(&self) -> String {
self.from.display_name.as_deref().unwrap_or_default().to_owned()
}
pub fn get_header(&self, header: &str) -> String {
self.get_headers().get_all_values(header).join(" ")
}
@ -151,7 +160,8 @@ impl Eq for EasyMail<'_> {}
impl Hash for EasyMail<'_> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
self.from.hash(state);
self.from.display_name.hash(state);
self.from.addr.hash(state);
self.subject.hash(state);
}
}
@ -164,6 +174,76 @@ impl<'a> Deref for EasyMail<'a> {
}
}
impl TreeEntry for &EasyMail<'_> {
fn display(&self, width: usize) -> SpannedString<Style> {
if self.is_pseudo() {
return self.subject.clone().into();
}
let from = self.from();
let mut line = self.subject.clone();
let mut i = width.saturating_sub(1 + from.len() + 1 + self.date_iso.len());
while i <= line.len() && !line.is_char_boundary(i) {
if i == 0 {
break;
}
i -= 1;
}
line.truncate(i);
let subj_len = line.len();
while line.len() < i {
line.push(' ');
}
line.push(' ');
line += &from;
line.push(' ');
line += &self.date_iso;
let spans = vec![
IndexedSpan {
content: IndexedCow::Borrowed {
start: 0,
end: subj_len
},
attr: Style::default(),
width: subj_len
},
IndexedSpan {
content: IndexedCow::Borrowed {
start: 0,
end: 0
},
attr: Style::default(),
width: line.len() - subj_len - from.len() - self.date_iso.len() - 1
},
IndexedSpan {
content: IndexedCow::Borrowed {
start: line.len() - self.date_iso.len() - 1 - from.len(),
end: line.len() - self.date_iso.len() - 1
},
attr: Style::default(),
width: from.len()
},
IndexedSpan {
content: IndexedCow::Borrowed {
start: 0,
end: 0
},
attr: Style::default(),
width: 1
},
IndexedSpan {
content: IndexedCow::Borrowed {
start: line.len() - self.date_iso.len(),
end: line.len()
},
attr: Style::default(),
width: self.date_iso.len()
},
];
SpannedString::with_spans(&line, spans)
}
}
pub trait MailExtension {
fn get_tree_structure<'a>(&'a self, graph: &mut Graph<&'a ParsedMail<'a>, ()>, parent: Option<NodeIndex>);
fn print_tree_structure(&self, depth: usize, counter: &mut usize);
@ -232,7 +312,7 @@ impl MaildirExtension for Maildir {
let flags = maile.flags().to_owned();
let mail = maile.parsed()?;
let headers = mail.get_headers();
let from = headers.get_all_values("From").join(" ");
let from = addrparse(&headers.get_all_values("From").join(" "))?.extract_single_info().context("failed to extract from")?;
let subject = headers.get_all_values("Subject").join(" ");
let date = headers.get_all_values("Date").join(" ");
let date = dateparse(&date).map(|x|
@ -259,7 +339,7 @@ impl MaildirExtension for Maildir {
let flags = maile.flags().to_owned();
let mail = maile.parsed()?;
let headers = mail.get_headers();
let from = headers.get_all_values("From").join(" ");
let from = addrparse(&headers.get_all_values("From").join(" "))?.extract_single_info().context("failed to extract from")?;
let subject = headers.get_all_values("Subject").join(" ");
let date = headers.get_all_values("Date").join(" ");
let date = dateparse(&date).map(|x|