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