mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-21 18:40:19 +00:00
wip: style thread tree using crossterm
This commit is contained in:
parent
55ba892436
commit
2eff215934
3 changed files with 134 additions and 48 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -1006,7 +1006,10 @@ dependencies = [
|
|||
"bitflags 2.5.0",
|
||||
"crossterm_winapi",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot 0.12.1",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
|
@ -2063,6 +2066,7 @@ dependencies = [
|
|||
"color-eyre",
|
||||
"comfy-table",
|
||||
"console",
|
||||
"crossterm 0.27.0",
|
||||
"dirs 4.0.0",
|
||||
"email-lib",
|
||||
"email_address",
|
||||
|
|
|
@ -54,6 +54,7 @@ clap_mangen = "0.2"
|
|||
color-eyre = "0.6.3"
|
||||
comfy-table = "7.1.1"
|
||||
console = "0.15.2"
|
||||
crossterm = "0.27"
|
||||
dirs = "4"
|
||||
email-lib = { version = "=0.24.1", default-features = false, features = ["derive", "tracing"] }
|
||||
email_address = "0.2.4"
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
use ariadne::{Color, Label, Report, ReportKind, Source};
|
||||
use ariadne::{Label, Report, ReportKind, Source};
|
||||
use clap::Parser;
|
||||
use color_eyre::Result;
|
||||
use crossterm::{
|
||||
cursor::{self, MoveToColumn},
|
||||
style::{Attribute, Color, Print, ResetColor, SetAttribute, SetForegroundColor},
|
||||
terminal, ExecutableCommand,
|
||||
};
|
||||
use email::{
|
||||
account::config::AccountConfig,
|
||||
backend::feature::BackendFeatureSource,
|
||||
email::search_query,
|
||||
envelope::list::ListEnvelopesOptions,
|
||||
envelope::{list::ListEnvelopesOptions, ThreadedEnvelope},
|
||||
search_query::{filter::SearchEmailsFilterQuery, SearchEmailsQuery},
|
||||
};
|
||||
use petgraph::{graphmap::DiGraphMap, visit::IntoNodeIdentifiers, Direction};
|
||||
|
@ -172,42 +178,55 @@ impl ThreadEnvelopesCommand {
|
|||
)
|
||||
.await?;
|
||||
|
||||
// let query = self
|
||||
// .query
|
||||
// .map(|query| query.join(" ").parse::<SearchEmailsQuery>());
|
||||
// let query = match query {
|
||||
// None => None,
|
||||
// Some(Ok(query)) => Some(query),
|
||||
// Some(Err(main_err)) => {
|
||||
// let source = "query";
|
||||
// let search_query::error::Error::ParseError(errs, query) = &main_err;
|
||||
// for err in errs {
|
||||
// Report::build(ReportKind::Error, source, err.span().start)
|
||||
// .with_message(main_err.to_string())
|
||||
// .with_label(
|
||||
// Label::new((source, err.span().into_range()))
|
||||
// .with_message(err.reason().to_string())
|
||||
// .with_color(Color::Red),
|
||||
// )
|
||||
// .finish()
|
||||
// .eprint((source, Source::from(&query)))
|
||||
// .unwrap();
|
||||
// }
|
||||
let query = self
|
||||
.query
|
||||
.map(|query| query.join(" ").parse::<SearchEmailsQuery>());
|
||||
let query = match query {
|
||||
None => None,
|
||||
Some(Ok(query)) => Some(query),
|
||||
Some(Err(main_err)) => {
|
||||
let source = "query";
|
||||
let search_query::error::Error::ParseError(errs, query) = &main_err;
|
||||
for err in errs {
|
||||
Report::build(ReportKind::Error, source, err.span().start)
|
||||
.with_message(main_err.to_string())
|
||||
.with_label(
|
||||
Label::new((source, err.span().into_range()))
|
||||
.with_message(err.reason().to_string())
|
||||
.with_color(ariadne::Color::Red),
|
||||
)
|
||||
.finish()
|
||||
.eprint((source, Source::from(&query)))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// exit(0)
|
||||
// }
|
||||
// };
|
||||
exit(0)
|
||||
}
|
||||
};
|
||||
|
||||
let opts = ListEnvelopesOptions {
|
||||
page,
|
||||
page_size,
|
||||
query: None,
|
||||
query,
|
||||
};
|
||||
|
||||
let envelopes = backend.thread_envelopes(folder, opts).await?;
|
||||
|
||||
let mut stdout = std::io::stdout();
|
||||
write_tree(&mut stdout, envelopes.graph(), "root", String::new(), 0)?;
|
||||
write_tree(
|
||||
&account_config,
|
||||
&mut stdout,
|
||||
envelopes.graph(),
|
||||
ThreadedEnvelope {
|
||||
id: "0",
|
||||
message_id: "0",
|
||||
from: "",
|
||||
subject: "",
|
||||
date: Default::default(),
|
||||
},
|
||||
String::new(),
|
||||
0,
|
||||
)?;
|
||||
stdout.flush()?;
|
||||
|
||||
// printer.print_table(envelopes, self.table_max_width)?;
|
||||
|
@ -217,9 +236,10 @@ impl ThreadEnvelopesCommand {
|
|||
}
|
||||
|
||||
pub fn write_tree(
|
||||
config: &AccountConfig,
|
||||
w: &mut impl std::io::Write,
|
||||
graph: &DiGraphMap<&str, u8>,
|
||||
parent: &str,
|
||||
graph: &DiGraphMap<ThreadedEnvelope<'_>, u8>,
|
||||
parent: ThreadedEnvelope<'_>,
|
||||
pad: String,
|
||||
weight: u8,
|
||||
) -> std::io::Result<()> {
|
||||
|
@ -234,7 +254,50 @@ pub fn write_tree(
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
writeln!(w, "{parent}")?;
|
||||
if parent.id == "0" {
|
||||
w.execute(Print("root"))?;
|
||||
} else {
|
||||
w.execute(SetForegroundColor(Color::Red))?
|
||||
.execute(Print(parent.id))?
|
||||
.execute(SetForegroundColor(Color::DarkGrey))?
|
||||
.execute(Print(") "))?
|
||||
.execute(ResetColor)?;
|
||||
|
||||
if !parent.subject.is_empty() {
|
||||
w.execute(SetForegroundColor(Color::Green))?
|
||||
.execute(Print(parent.subject))?
|
||||
.execute(ResetColor)?
|
||||
.execute(Print(" "))?;
|
||||
}
|
||||
|
||||
if !parent.from.is_empty() {
|
||||
w.execute(SetForegroundColor(Color::DarkGrey))?
|
||||
.execute(Print("<"))?
|
||||
.execute(SetForegroundColor(Color::Blue))?
|
||||
.execute(Print(parent.from))?
|
||||
.execute(SetForegroundColor(Color::DarkGrey))?
|
||||
.execute(Print(">"))?
|
||||
.execute(ResetColor)?;
|
||||
}
|
||||
|
||||
let date = parent.format_date(config);
|
||||
let cursor_date_begin_col = terminal::size()?.0 - date.len() as u16;
|
||||
|
||||
w.execute(Print(" "))?
|
||||
.execute(SetForegroundColor(Color::DarkGrey))?
|
||||
.execute(Print("·".repeat(
|
||||
(cursor_date_begin_col - cursor::position()?.0 - 1) as usize,
|
||||
)))?
|
||||
.execute(ResetColor)?
|
||||
.execute(Print(" "))?;
|
||||
|
||||
w.execute(MoveToColumn(terminal::size()?.0 - date.len() as u16))?
|
||||
.execute(SetForegroundColor(Color::DarkYellow))?
|
||||
.execute(Print(date))?
|
||||
.execute(ResetColor)?;
|
||||
}
|
||||
|
||||
writeln!(w)?;
|
||||
|
||||
let edges_count = edges.len();
|
||||
for (i, b) in edges.into_iter().enumerate() {
|
||||
|
@ -244,9 +307,11 @@ pub fn write_tree(
|
|||
} else {
|
||||
('│', '├')
|
||||
};
|
||||
|
||||
write!(w, "{pad}{y}─ ")?;
|
||||
|
||||
let pad = format!("{pad}{x} ");
|
||||
write_tree(w, graph, b, pad, weight + 1)?;
|
||||
write_tree(config, w, graph, b, pad, weight + 1)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -254,19 +319,33 @@ pub fn write_tree(
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use email::{account::config::AccountConfig, envelope::ThreadedEnvelope};
|
||||
use petgraph::graphmap::DiGraphMap;
|
||||
|
||||
use super::write_tree;
|
||||
|
||||
macro_rules! e {
|
||||
($id:literal) => {
|
||||
ThreadedEnvelope {
|
||||
id: $id,
|
||||
message_id: $id,
|
||||
from: "",
|
||||
subject: "",
|
||||
date: Default::default(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_1() {
|
||||
let config = AccountConfig::default();
|
||||
let mut buf = Vec::new();
|
||||
let mut graph = DiGraphMap::new();
|
||||
graph.add_edge("0", "1", 0);
|
||||
graph.add_edge("0", "2", 0);
|
||||
graph.add_edge("0", "3", 0);
|
||||
graph.add_edge(e!("0"), e!("1"), 0);
|
||||
graph.add_edge(e!("0"), e!("2"), 0);
|
||||
graph.add_edge(e!("0"), e!("3"), 0);
|
||||
|
||||
write_tree(&mut buf, &graph, "0", String::new(), 0).unwrap();
|
||||
write_tree(&config, &mut buf, &graph, e!("0"), String::new(), 0).unwrap();
|
||||
let buf = String::from_utf8_lossy(&buf);
|
||||
|
||||
let expected = "
|
||||
|
@ -280,13 +359,14 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn tree_2() {
|
||||
let config = AccountConfig::default();
|
||||
let mut buf = Vec::new();
|
||||
let mut graph = DiGraphMap::new();
|
||||
graph.add_edge("0", "1", 0);
|
||||
graph.add_edge("1", "2", 1);
|
||||
graph.add_edge("1", "3", 1);
|
||||
graph.add_edge(e!("0"), e!("1"), 0);
|
||||
graph.add_edge(e!("1"), e!("2"), 1);
|
||||
graph.add_edge(e!("1"), e!("3"), 1);
|
||||
|
||||
write_tree(&mut buf, &graph, "0", String::new(), 0).unwrap();
|
||||
write_tree(&config, &mut buf, &graph, e!("0"), String::new(), 0).unwrap();
|
||||
let buf = String::from_utf8_lossy(&buf);
|
||||
|
||||
let expected = "
|
||||
|
@ -300,17 +380,18 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn tree_3() {
|
||||
let config = AccountConfig::default();
|
||||
let mut buf = Vec::new();
|
||||
let mut graph = DiGraphMap::new();
|
||||
graph.add_edge("0", "1", 0);
|
||||
graph.add_edge("1", "2", 1);
|
||||
graph.add_edge("2", "22", 2);
|
||||
graph.add_edge("1", "3", 1);
|
||||
graph.add_edge("0", "4", 0);
|
||||
graph.add_edge("4", "5", 1);
|
||||
graph.add_edge("5", "6", 2);
|
||||
graph.add_edge(e!("0"), e!("1"), 0);
|
||||
graph.add_edge(e!("1"), e!("2"), 1);
|
||||
graph.add_edge(e!("2"), e!("22"), 2);
|
||||
graph.add_edge(e!("1"), e!("3"), 1);
|
||||
graph.add_edge(e!("0"), e!("4"), 0);
|
||||
graph.add_edge(e!("4"), e!("5"), 1);
|
||||
graph.add_edge(e!("5"), e!("6"), 2);
|
||||
|
||||
write_tree(&mut buf, &graph, "0", String::new(), 0).unwrap();
|
||||
write_tree(&config, &mut buf, &graph, e!("0"), String::new(), 0).unwrap();
|
||||
let buf = String::from_utf8_lossy(&buf);
|
||||
|
||||
let expected = "
|
||||
|
|
Loading…
Reference in a new issue