mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-24 12:00:22 +00:00
make tables more customizable
All tables can customize the color of their column, and the envelopes table can customize its flag chars.
This commit is contained in:
parent
daf2c7c87a
commit
8ccabf1fc0
14 changed files with 535 additions and 166 deletions
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added `account.list.table.preset` global config option, `accounts.<name>.folder.list.table.preset` and `accounts.<name>.envelope.list.table.preset` account config options.
|
||||
|
||||
These options customize the shape of tables, see examples at [`comfy_table::presets`](https://docs.rs/comfy-table/latest/comfy_table/presets/index.html). Defaults to `"|| |-||| "`, which corresponds to [`comfy_table::presets::ASCII_MARKDOWN`](https://docs.rs/comfy-table/latest/comfy_table/presets/constant.ASCII_MARKDOWN.html).
|
||||
|
||||
- Added `account.list.table.name-color` config option to customize the color used for the accounts' `NAME` column (defaults to `green`).
|
||||
- Added `account.list.table.backends-color` config option to customize the color used for the folders' `BACKENDS` column (defaults to `blue`).
|
||||
- Added `account.list.table.default-color` config option to customize the color used for the folders' `DEFAULT` column (defaults to `reset`).
|
||||
- Added `accounts.<name>.folder.list.table.name-color` account config option to customize the color used for the folders' `NAME` column (defaults to `blue`).
|
||||
- Added `accounts.<name>.folder.list.table.desc-color` account config option to customize the color used for the folders' `DESC` column (defaults to `green`).
|
||||
- Added `accounts.<name>.envelope.list.table.id-color` account config option to customize the color used for the envelopes' `ID` column (defaults to `red`).
|
||||
- Added `accounts.<name>.envelope.list.table.flags-color` account config option to customize the color used for the envelopes' `FLAGS` column (defaults to `reset`).
|
||||
- Added `accounts.<name>.envelope.list.table.subject-color` account config option to customize the color used for the envelopes' `SUBJECT` column (defaults to `green`).
|
||||
- Added `accounts.<name>.envelope.list.table.sender-color` account config option to customize the color used for the envelopes' `FROM` column (defaults to `blue`).
|
||||
- Added `accounts.<name>.envelope.list.table.date-color` account config option to customize the color used for the envelopes' `DATE` column (defaults to `dark_yellow`).
|
||||
- Added `accounts.<name>.envelope.list.table.unseen-char` account config option to customize the char used for unseen envelopes (defaults to `*`).
|
||||
- Added `accounts.<name>.envelope.list.table.replied-char` account config option to customize the char used for replied envelopes (defaults to `R`).
|
||||
- Added `accounts.<name>.envelope.list.table.flagged-char` account config option to customize the char used for flagged envelopes (defaults to `!`).
|
||||
- Added `accounts.<name>.envelope.list.table.attachment-char` account config option to customize the char used for envelopes with at least one attachment (defaults to `@`).
|
||||
|
||||
## [1.0.0-beta.4] - 2024-04-16
|
||||
|
||||
### Added
|
||||
|
|
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -418,6 +418,9 @@ name = "bitflags"
|
|||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
|
@ -885,6 +888,7 @@ dependencies = [
|
|||
"libc",
|
||||
"mio 0.8.11",
|
||||
"parking_lot 0.12.3",
|
||||
"serde",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
|
|
|
@ -56,7 +56,7 @@ clap_mangen = "0.2"
|
|||
color-eyre = "0.6.3"
|
||||
comfy-table = "7.1.1"
|
||||
console = "0.15.2"
|
||||
crossterm = "0.27"
|
||||
crossterm = { version = "0.27", features = ["serde"] }
|
||||
dirs = "4"
|
||||
email-lib = { version = "=0.25.0", default-features = false, features = ["derive", "thread", "tracing"] }
|
||||
email_address = "0.2.4"
|
||||
|
|
|
@ -28,7 +28,12 @@ impl AccountListCommand {
|
|||
info!("executing list accounts command");
|
||||
|
||||
let accounts = Accounts::from(config.accounts.iter());
|
||||
let table = AccountsTable::from(accounts).with_some_width(self.table_max_width);
|
||||
let table = AccountsTable::from(accounts)
|
||||
.with_some_width(self.table_max_width)
|
||||
.with_some_preset(config.account_list_table_preset())
|
||||
.with_some_name_color(config.account_list_table_name_color())
|
||||
.with_some_backends_color(config.account_list_table_backends_color())
|
||||
.with_some_default_color(config.account_list_table_default_color());
|
||||
|
||||
printer.out(table)?;
|
||||
Ok(())
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
//! This module contains the raw deserialized representation of an
|
||||
//! account in the accounts section of the user configuration file.
|
||||
|
||||
use comfy_table::presets;
|
||||
use crossterm::style::Color;
|
||||
#[cfg(feature = "pgp")]
|
||||
use email::account::config::pgp::PgpConfig;
|
||||
#[cfg(feature = "imap")]
|
||||
|
@ -21,7 +23,7 @@ use std::{collections::HashSet, path::PathBuf};
|
|||
|
||||
use crate::{
|
||||
backend::BackendKind, envelope::config::EnvelopeConfig, flag::config::FlagConfig,
|
||||
folder::config::FolderConfig, message::config::MessageConfig,
|
||||
folder::config::FolderConfig, message::config::MessageConfig, ui::map_color,
|
||||
};
|
||||
|
||||
/// Represents all existing kind of account config.
|
||||
|
@ -58,6 +60,110 @@ pub struct TomlAccountConfig {
|
|||
}
|
||||
|
||||
impl TomlAccountConfig {
|
||||
pub fn folder_list_table_preset(&self) -> Option<String> {
|
||||
self.folder
|
||||
.as_ref()
|
||||
.and_then(|folder| folder.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.preset.clone())
|
||||
}
|
||||
|
||||
pub fn folder_list_table_name_color(&self) -> Option<Color> {
|
||||
self.folder
|
||||
.as_ref()
|
||||
.and_then(|folder| folder.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.name_color)
|
||||
}
|
||||
|
||||
pub fn folder_list_table_desc_color(&self) -> Option<Color> {
|
||||
self.folder
|
||||
.as_ref()
|
||||
.and_then(|folder| folder.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.desc_color)
|
||||
}
|
||||
|
||||
pub fn envelope_list_table_preset(&self) -> Option<String> {
|
||||
self.envelope
|
||||
.as_ref()
|
||||
.and_then(|env| env.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.preset.clone())
|
||||
}
|
||||
|
||||
pub fn envelope_list_table_unseen_char(&self) -> Option<char> {
|
||||
self.envelope
|
||||
.as_ref()
|
||||
.and_then(|env| env.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.unseen_char)
|
||||
}
|
||||
|
||||
pub fn envelope_list_table_replied_char(&self) -> Option<char> {
|
||||
self.envelope
|
||||
.as_ref()
|
||||
.and_then(|env| env.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.replied_char)
|
||||
}
|
||||
|
||||
pub fn envelope_list_table_flagged_char(&self) -> Option<char> {
|
||||
self.envelope
|
||||
.as_ref()
|
||||
.and_then(|env| env.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.flagged_char)
|
||||
}
|
||||
|
||||
pub fn envelope_list_table_attachment_char(&self) -> Option<char> {
|
||||
self.envelope
|
||||
.as_ref()
|
||||
.and_then(|env| env.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.attachment_char)
|
||||
}
|
||||
|
||||
pub fn envelope_list_table_id_color(&self) -> Option<Color> {
|
||||
self.envelope
|
||||
.as_ref()
|
||||
.and_then(|env| env.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.id_color)
|
||||
}
|
||||
|
||||
pub fn envelope_list_table_flags_color(&self) -> Option<Color> {
|
||||
self.envelope
|
||||
.as_ref()
|
||||
.and_then(|env| env.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.flags_color)
|
||||
}
|
||||
|
||||
pub fn envelope_list_table_subject_color(&self) -> Option<Color> {
|
||||
self.envelope
|
||||
.as_ref()
|
||||
.and_then(|env| env.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.subject_color)
|
||||
}
|
||||
|
||||
pub fn envelope_list_table_sender_color(&self) -> Option<Color> {
|
||||
self.envelope
|
||||
.as_ref()
|
||||
.and_then(|env| env.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.sender_color)
|
||||
}
|
||||
|
||||
pub fn envelope_list_table_date_color(&self) -> Option<Color> {
|
||||
self.envelope
|
||||
.as_ref()
|
||||
.and_then(|env| env.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.date_color)
|
||||
}
|
||||
|
||||
pub fn add_folder_kind(&self) -> Option<&BackendKind> {
|
||||
self.folder
|
||||
.as_ref()
|
||||
|
@ -228,3 +334,30 @@ impl TomlAccountConfig {
|
|||
used_backends
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct ListAccountsTableConfig {
|
||||
pub preset: Option<String>,
|
||||
pub name_color: Option<Color>,
|
||||
pub backends_color: Option<Color>,
|
||||
pub default_color: Option<Color>,
|
||||
}
|
||||
|
||||
impl ListAccountsTableConfig {
|
||||
pub fn preset(&self) -> &str {
|
||||
self.preset.as_deref().unwrap_or(presets::ASCII_MARKDOWN)
|
||||
}
|
||||
|
||||
pub fn name_color(&self) -> comfy_table::Color {
|
||||
map_color(self.name_color.unwrap_or(Color::Green))
|
||||
}
|
||||
|
||||
pub fn backends_color(&self) -> comfy_table::Color {
|
||||
map_color(self.backends_color.unwrap_or(Color::Blue))
|
||||
}
|
||||
|
||||
pub fn default_color(&self) -> comfy_table::Color {
|
||||
map_color(self.default_color.unwrap_or(Color::Reset))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,12 @@ pub mod config;
|
|||
#[cfg(feature = "wizard")]
|
||||
pub(crate) mod wizard;
|
||||
|
||||
use comfy_table::{presets, Attribute, Cell, Color, ContentArrangement, Row, Table};
|
||||
use comfy_table::{Cell, ContentArrangement, Row, Table};
|
||||
use crossterm::style::Color;
|
||||
use serde::{Serialize, Serializer};
|
||||
use std::{collections::hash_map::Iter, fmt, ops::Deref};
|
||||
|
||||
use self::config::TomlAccountConfig;
|
||||
use self::config::{ListAccountsTableConfig, TomlAccountConfig};
|
||||
|
||||
/// Represents the printable account.
|
||||
#[derive(Debug, Default, PartialEq, Eq, Serialize)]
|
||||
|
@ -30,12 +31,12 @@ impl Account {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_row(&self) -> Row {
|
||||
pub fn to_row(&self, config: &ListAccountsTableConfig) -> Row {
|
||||
let mut row = Row::new();
|
||||
|
||||
row.add_cell(Cell::new(&self.name).fg(Color::Green));
|
||||
row.add_cell(Cell::new(&self.backend).fg(Color::Blue));
|
||||
row.add_cell(Cell::new(if self.default { "yes" } else { "" }).fg(Color::White));
|
||||
row.add_cell(Cell::new(&self.name).fg(config.name_color()));
|
||||
row.add_cell(Cell::new(&self.backend).fg(config.backends_color()));
|
||||
row.add_cell(Cell::new(if self.default { "yes" } else { "" }).fg(config.default_color()));
|
||||
|
||||
row
|
||||
}
|
||||
|
@ -51,24 +52,6 @@ impl fmt::Display for Account {
|
|||
#[derive(Debug, Default, Serialize)]
|
||||
pub struct Accounts(Vec<Account>);
|
||||
|
||||
impl Accounts {
|
||||
pub fn to_table(&self) -> Table {
|
||||
let mut table = Table::new();
|
||||
|
||||
table
|
||||
.load_preset(presets::NOTHING)
|
||||
.set_content_arrangement(ContentArrangement::DynamicFullWidth)
|
||||
.set_header(Row::from([
|
||||
Cell::new("NAME").add_attribute(Attribute::Reverse),
|
||||
Cell::new("BACKENDS").add_attribute(Attribute::Reverse),
|
||||
Cell::new("DEFAULT").add_attribute(Attribute::Reverse),
|
||||
]))
|
||||
.add_rows(self.iter().map(Account::to_row));
|
||||
|
||||
table
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Accounts {
|
||||
type Target = Vec<Account>;
|
||||
|
||||
|
@ -98,7 +81,7 @@ impl From<Iter<'_, String, TomlAccountConfig>> for Accounts {
|
|||
}
|
||||
|
||||
#[cfg(feature = "notmuch")]
|
||||
if account.imap.is_some() {
|
||||
if account.notmuch.is_some() {
|
||||
if !backends.is_empty() {
|
||||
backends.push_str(", ")
|
||||
}
|
||||
|
@ -135,6 +118,7 @@ impl From<Iter<'_, String, TomlAccountConfig>> for Accounts {
|
|||
pub struct AccountsTable {
|
||||
accounts: Accounts,
|
||||
width: Option<u16>,
|
||||
config: ListAccountsTableConfig,
|
||||
}
|
||||
|
||||
impl AccountsTable {
|
||||
|
@ -142,6 +126,26 @@ impl AccountsTable {
|
|||
self.width = width;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_preset(mut self, preset: Option<String>) -> Self {
|
||||
self.config.preset = preset;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_name_color(mut self, color: Option<Color>) -> Self {
|
||||
self.config.name_color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_backends_color(mut self, color: Option<Color>) -> Self {
|
||||
self.config.backends_color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_default_color(mut self, color: Option<Color>) -> Self {
|
||||
self.config.default_color = color;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Accounts> for AccountsTable {
|
||||
|
@ -149,13 +153,28 @@ impl From<Accounts> for AccountsTable {
|
|||
Self {
|
||||
accounts,
|
||||
width: None,
|
||||
config: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AccountsTable {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut table = self.accounts.to_table();
|
||||
let mut table = Table::new();
|
||||
|
||||
table
|
||||
.load_preset(self.config.preset())
|
||||
.set_content_arrangement(ContentArrangement::DynamicFullWidth)
|
||||
.set_header(Row::from([
|
||||
Cell::new("NAME"),
|
||||
Cell::new("BACKENDS"),
|
||||
Cell::new("DEFAULT"),
|
||||
]))
|
||||
.add_rows(
|
||||
self.accounts
|
||||
.iter()
|
||||
.map(|account| account.to_row(&self.config)),
|
||||
);
|
||||
|
||||
if let Some(width) = self.width {
|
||||
table.set_width(width);
|
||||
|
|
|
@ -5,6 +5,7 @@ use color_eyre::{
|
|||
eyre::{bail, eyre, Context},
|
||||
Result,
|
||||
};
|
||||
use crossterm::style::Color;
|
||||
use dirs::{config_dir, home_dir};
|
||||
use email::{
|
||||
account::config::AccountConfig, config::Config, envelope::config::EnvelopeConfig,
|
||||
|
@ -17,7 +18,7 @@ use std::{collections::HashMap, fs, path::PathBuf, sync::Arc};
|
|||
use toml::{self, Value};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::account::config::TomlAccountConfig;
|
||||
use crate::account::config::{ListAccountsTableConfig, TomlAccountConfig};
|
||||
#[cfg(feature = "wizard")]
|
||||
use crate::wizard_warn;
|
||||
|
||||
|
@ -31,9 +32,42 @@ pub struct TomlConfig {
|
|||
pub signature_delim: Option<String>,
|
||||
pub downloads_dir: Option<PathBuf>,
|
||||
pub accounts: HashMap<String, TomlAccountConfig>,
|
||||
pub account: Option<AccountsConfig>,
|
||||
}
|
||||
|
||||
impl TomlConfig {
|
||||
pub fn account_list_table_preset(&self) -> Option<String> {
|
||||
self.account
|
||||
.as_ref()
|
||||
.and_then(|account| account.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.preset.clone())
|
||||
}
|
||||
|
||||
pub fn account_list_table_name_color(&self) -> Option<Color> {
|
||||
self.account
|
||||
.as_ref()
|
||||
.and_then(|account| account.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.name_color)
|
||||
}
|
||||
|
||||
pub fn account_list_table_backends_color(&self) -> Option<Color> {
|
||||
self.account
|
||||
.as_ref()
|
||||
.and_then(|account| account.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.backends_color)
|
||||
}
|
||||
|
||||
pub fn account_list_table_default_color(&self) -> Option<Color> {
|
||||
self.account
|
||||
.as_ref()
|
||||
.and_then(|account| account.list.as_ref())
|
||||
.and_then(|list| list.table.as_ref())
|
||||
.and_then(|table| table.default_color)
|
||||
}
|
||||
|
||||
/// Read and parse the TOML configuration at the given paths.
|
||||
///
|
||||
/// Returns an error if a configuration file cannot be read or if
|
||||
|
@ -266,3 +300,15 @@ pub fn path_parser(path: &str) -> Result<PathBuf, String> {
|
|||
.map(canonicalize::path)
|
||||
.map_err(|err| err.to_string())
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct AccountsConfig {
|
||||
pub list: Option<ListAccountsConfig>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct ListAccountsConfig {
|
||||
pub table: Option<ListAccountsTableConfig>,
|
||||
}
|
||||
|
|
|
@ -188,7 +188,18 @@ impl ListEnvelopesCommand {
|
|||
};
|
||||
|
||||
let envelopes = backend.list_envelopes(folder, opts).await?;
|
||||
let table = EnvelopesTable::from(envelopes).with_some_width(self.table_max_width);
|
||||
let table = EnvelopesTable::from(envelopes)
|
||||
.with_some_width(self.table_max_width)
|
||||
.with_some_preset(toml_account_config.envelope_list_table_preset())
|
||||
.with_some_unseen_char(toml_account_config.envelope_list_table_unseen_char())
|
||||
.with_some_replied_char(toml_account_config.envelope_list_table_replied_char())
|
||||
.with_some_flagged_char(toml_account_config.envelope_list_table_flagged_char())
|
||||
.with_some_attachment_char(toml_account_config.envelope_list_table_attachment_char())
|
||||
.with_some_id_color(toml_account_config.envelope_list_table_id_color())
|
||||
.with_some_flags_color(toml_account_config.envelope_list_table_flags_color())
|
||||
.with_some_subject_color(toml_account_config.envelope_list_table_subject_color())
|
||||
.with_some_sender_color(toml_account_config.envelope_list_table_sender_color())
|
||||
.with_some_date_color(toml_account_config.envelope_list_table_date_color());
|
||||
|
||||
printer.out(table)?;
|
||||
Ok(())
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use comfy_table::presets;
|
||||
use crossterm::style::Color;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::backend::BackendKind;
|
||||
use crate::{backend::BackendKind, ui::map_color};
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub struct EnvelopeConfig {
|
||||
|
@ -27,8 +29,10 @@ impl EnvelopeConfig {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct ListEnvelopesConfig {
|
||||
pub backend: Option<BackendKind>,
|
||||
pub table: Option<ListEnvelopesTableConfig>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub remote: email::envelope::list::config::EnvelopeListConfig,
|
||||
|
@ -46,6 +50,81 @@ impl ListEnvelopesConfig {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct ListEnvelopesTableConfig {
|
||||
pub preset: Option<String>,
|
||||
|
||||
pub unseen_char: Option<char>,
|
||||
pub replied_char: Option<char>,
|
||||
pub flagged_char: Option<char>,
|
||||
pub attachment_char: Option<char>,
|
||||
|
||||
pub id_color: Option<Color>,
|
||||
pub flags_color: Option<Color>,
|
||||
pub subject_color: Option<Color>,
|
||||
pub sender_color: Option<Color>,
|
||||
pub date_color: Option<Color>,
|
||||
}
|
||||
|
||||
impl ListEnvelopesTableConfig {
|
||||
pub fn preset(&self) -> &str {
|
||||
self.preset.as_deref().unwrap_or(presets::ASCII_MARKDOWN)
|
||||
}
|
||||
|
||||
pub fn replied_char(&self, replied: bool) -> char {
|
||||
if replied {
|
||||
self.replied_char.unwrap_or('R')
|
||||
} else {
|
||||
' '
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flagged_char(&self, flagged: bool) -> char {
|
||||
if flagged {
|
||||
self.flagged_char.unwrap_or('!')
|
||||
} else {
|
||||
' '
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attachment_char(&self, attachment: bool) -> char {
|
||||
if attachment {
|
||||
self.attachment_char.unwrap_or('@')
|
||||
} else {
|
||||
' '
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unseen_char(&self, unseen: bool) -> char {
|
||||
if unseen {
|
||||
self.unseen_char.unwrap_or('*')
|
||||
} else {
|
||||
' '
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id_color(&self) -> comfy_table::Color {
|
||||
map_color(self.id_color.unwrap_or(Color::Red))
|
||||
}
|
||||
|
||||
pub fn flags_color(&self) -> comfy_table::Color {
|
||||
map_color(self.flags_color.unwrap_or(Color::Reset))
|
||||
}
|
||||
|
||||
pub fn subject_color(&self) -> comfy_table::Color {
|
||||
map_color(self.subject_color.unwrap_or(Color::Green))
|
||||
}
|
||||
|
||||
pub fn sender_color(&self) -> comfy_table::Color {
|
||||
map_color(self.sender_color.unwrap_or(Color::Blue))
|
||||
}
|
||||
|
||||
pub fn date_color(&self) -> comfy_table::Color {
|
||||
map_color(self.date_color.unwrap_or(Color::DarkYellow))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub struct ThreadEnvelopesConfig {
|
||||
pub backend: Option<BackendKind>,
|
||||
|
|
|
@ -4,8 +4,12 @@ pub mod config;
|
|||
pub mod flag;
|
||||
|
||||
use color_eyre::Result;
|
||||
use comfy_table::{presets, Attribute, Cell, ContentArrangement, Row, Table};
|
||||
use crossterm::{cursor, style::Stylize, terminal};
|
||||
use comfy_table::{Attribute, Cell, ContentArrangement, Row, Table};
|
||||
use crossterm::{
|
||||
cursor,
|
||||
style::{Color, Stylize},
|
||||
terminal,
|
||||
};
|
||||
use email::{account::config::AccountConfig, envelope::ThreadedEnvelope};
|
||||
use petgraph::graphmap::DiGraphMap;
|
||||
use serde::{Serialize, Serializer};
|
||||
|
@ -16,6 +20,8 @@ use crate::{
|
|||
flag::{Flag, Flags},
|
||||
};
|
||||
|
||||
use self::config::ListEnvelopesTableConfig;
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
pub struct Mailbox {
|
||||
pub name: Option<String>,
|
||||
|
@ -33,72 +39,11 @@ pub struct Envelope {
|
|||
pub has_attachment: bool,
|
||||
}
|
||||
|
||||
impl From<Envelope> for Row {
|
||||
fn from(envelope: Envelope) -> Self {
|
||||
impl Envelope {
|
||||
fn to_row(&self, config: &ListEnvelopesTableConfig) -> Row {
|
||||
let mut all_attributes = vec![];
|
||||
|
||||
let unseen = !envelope.flags.contains(&Flag::Seen);
|
||||
if unseen {
|
||||
all_attributes.push(Attribute::Bold)
|
||||
}
|
||||
|
||||
let flags = {
|
||||
let mut flags = String::new();
|
||||
flags.push(if !unseen { ' ' } else { '✷' });
|
||||
flags.push(if envelope.flags.contains(&Flag::Answered) {
|
||||
'↵'
|
||||
} else {
|
||||
' '
|
||||
});
|
||||
flags.push(if envelope.flags.contains(&Flag::Flagged) {
|
||||
'⚑'
|
||||
} else {
|
||||
' '
|
||||
});
|
||||
flags
|
||||
};
|
||||
|
||||
let mut row = Row::new();
|
||||
|
||||
row.add_cell(
|
||||
Cell::new(envelope.id)
|
||||
.add_attributes(all_attributes.clone())
|
||||
.fg(comfy_table::Color::Red),
|
||||
)
|
||||
.add_cell(
|
||||
Cell::new(flags)
|
||||
.add_attributes(all_attributes.clone())
|
||||
.fg(comfy_table::Color::White),
|
||||
)
|
||||
.add_cell(
|
||||
Cell::new(envelope.subject)
|
||||
.add_attributes(all_attributes.clone())
|
||||
.fg(comfy_table::Color::Green),
|
||||
)
|
||||
.add_cell(
|
||||
Cell::new(if let Some(name) = envelope.from.name {
|
||||
name
|
||||
} else {
|
||||
envelope.from.addr
|
||||
})
|
||||
.add_attributes(all_attributes.clone())
|
||||
.fg(comfy_table::Color::Blue),
|
||||
)
|
||||
.add_cell(
|
||||
Cell::new(envelope.date)
|
||||
.add_attributes(all_attributes)
|
||||
.fg(comfy_table::Color::Yellow),
|
||||
);
|
||||
|
||||
row
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Envelope> for Row {
|
||||
fn from(envelope: &Envelope) -> Self {
|
||||
let mut all_attributes = vec![];
|
||||
|
||||
let unseen = !envelope.flags.contains(&Flag::Seen);
|
||||
let unseen = !self.flags.contains(&Flag::Seen);
|
||||
if unseen {
|
||||
all_attributes.push(Attribute::Bold)
|
||||
}
|
||||
|
@ -106,55 +51,45 @@ impl From<&Envelope> for Row {
|
|||
let flags = {
|
||||
let mut flags = String::new();
|
||||
|
||||
flags.push(if !unseen { ' ' } else { '✷' });
|
||||
|
||||
flags.push(if envelope.flags.contains(&Flag::Answered) {
|
||||
'↵'
|
||||
} else {
|
||||
' '
|
||||
});
|
||||
|
||||
flags.push(if envelope.flags.contains(&Flag::Flagged) {
|
||||
'⚑'
|
||||
} else {
|
||||
' '
|
||||
});
|
||||
|
||||
flags.push(if envelope.has_attachment { '📎' } else { ' ' });
|
||||
flags.push(config.unseen_char(unseen));
|
||||
flags.push(config.replied_char(self.flags.contains(&Flag::Answered)));
|
||||
flags.push(config.flagged_char(self.flags.contains(&Flag::Flagged)));
|
||||
flags.push(config.attachment_char(self.has_attachment));
|
||||
|
||||
flags
|
||||
};
|
||||
|
||||
let mut row = Row::new();
|
||||
row.max_height(1);
|
||||
|
||||
row.add_cell(
|
||||
Cell::new(&envelope.id)
|
||||
Cell::new(&self.id)
|
||||
.add_attributes(all_attributes.clone())
|
||||
.fg(comfy_table::Color::Red),
|
||||
.fg(config.id_color()),
|
||||
)
|
||||
.add_cell(
|
||||
Cell::new(flags)
|
||||
.add_attributes(all_attributes.clone())
|
||||
.fg(comfy_table::Color::White),
|
||||
.fg(config.flags_color()),
|
||||
)
|
||||
.add_cell(
|
||||
Cell::new(&envelope.subject)
|
||||
Cell::new(&self.subject)
|
||||
.add_attributes(all_attributes.clone())
|
||||
.fg(comfy_table::Color::Green),
|
||||
.fg(config.subject_color()),
|
||||
)
|
||||
.add_cell(
|
||||
Cell::new(if let Some(name) = &envelope.from.name {
|
||||
Cell::new(if let Some(name) = &self.from.name {
|
||||
name
|
||||
} else {
|
||||
&envelope.from.addr
|
||||
&self.from.addr
|
||||
})
|
||||
.add_attributes(all_attributes.clone())
|
||||
.fg(comfy_table::Color::Blue),
|
||||
.fg(config.sender_color()),
|
||||
)
|
||||
.add_cell(
|
||||
Cell::new(&envelope.date)
|
||||
Cell::new(&self.date)
|
||||
.add_attributes(all_attributes)
|
||||
.fg(comfy_table::Color::Yellow),
|
||||
.fg(config.date_color()),
|
||||
);
|
||||
|
||||
row
|
||||
|
@ -193,24 +128,6 @@ impl Envelopes {
|
|||
|
||||
Ok(Envelopes(envelopes))
|
||||
}
|
||||
|
||||
pub fn to_table(&self) -> Table {
|
||||
let mut table = Table::new();
|
||||
|
||||
table
|
||||
.load_preset(presets::NOTHING)
|
||||
.set_content_arrangement(ContentArrangement::DynamicFullWidth)
|
||||
.set_header(Row::from([
|
||||
Cell::new("ID").add_attribute(Attribute::Reverse),
|
||||
Cell::new("FLAGS").add_attribute(Attribute::Reverse),
|
||||
Cell::new("SUBJECT").add_attribute(Attribute::Reverse),
|
||||
Cell::new("FROM").add_attribute(Attribute::Reverse),
|
||||
Cell::new("DATE").add_attribute(Attribute::Reverse),
|
||||
]))
|
||||
.add_rows(self.iter().map(Row::from));
|
||||
|
||||
table
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Envelopes {
|
||||
|
@ -224,6 +141,7 @@ impl Deref for Envelopes {
|
|||
pub struct EnvelopesTable {
|
||||
envelopes: Envelopes,
|
||||
width: Option<u16>,
|
||||
config: ListEnvelopesTableConfig,
|
||||
}
|
||||
|
||||
impl EnvelopesTable {
|
||||
|
@ -231,6 +149,56 @@ impl EnvelopesTable {
|
|||
self.width = width;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_preset(mut self, preset: Option<String>) -> Self {
|
||||
self.config.preset = preset;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_unseen_char(mut self, char: Option<char>) -> Self {
|
||||
self.config.unseen_char = char;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_replied_char(mut self, char: Option<char>) -> Self {
|
||||
self.config.replied_char = char;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_flagged_char(mut self, char: Option<char>) -> Self {
|
||||
self.config.flagged_char = char;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_attachment_char(mut self, char: Option<char>) -> Self {
|
||||
self.config.attachment_char = char;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_id_color(mut self, color: Option<Color>) -> Self {
|
||||
self.config.id_color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_flags_color(mut self, color: Option<Color>) -> Self {
|
||||
self.config.flags_color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_subject_color(mut self, color: Option<Color>) -> Self {
|
||||
self.config.subject_color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_sender_color(mut self, color: Option<Color>) -> Self {
|
||||
self.config.sender_color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_date_color(mut self, color: Option<Color>) -> Self {
|
||||
self.config.date_color = color;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Envelopes> for EnvelopesTable {
|
||||
|
@ -238,13 +206,26 @@ impl From<Envelopes> for EnvelopesTable {
|
|||
Self {
|
||||
envelopes,
|
||||
width: None,
|
||||
config: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EnvelopesTable {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut table = self.envelopes.to_table();
|
||||
let mut table = Table::new();
|
||||
|
||||
table
|
||||
.load_preset(self.config.preset())
|
||||
.set_content_arrangement(ContentArrangement::DynamicFullWidth)
|
||||
.set_header(Row::from([
|
||||
Cell::new("ID"),
|
||||
Cell::new("FLAGS"),
|
||||
Cell::new("SUBJECT"),
|
||||
Cell::new("FROM"),
|
||||
Cell::new("DATE"),
|
||||
]))
|
||||
.add_rows(self.envelopes.iter().map(|env| env.to_row(&self.config)));
|
||||
|
||||
if let Some(width) = self.width {
|
||||
table.set_width(width);
|
||||
|
|
|
@ -47,7 +47,11 @@ impl FolderListCommand {
|
|||
.await?;
|
||||
|
||||
let folders = Folders::from(backend.list_folders().await?);
|
||||
let table = FoldersTable::from(folders).with_some_width(self.table_max_width);
|
||||
let table = FoldersTable::from(folders)
|
||||
.with_some_width(self.table_max_width)
|
||||
.with_some_preset(toml_account_config.folder_list_table_preset())
|
||||
.with_some_name_color(toml_account_config.folder_list_table_name_color())
|
||||
.with_some_desc_color(toml_account_config.folder_list_table_desc_color());
|
||||
|
||||
printer.log(table)?;
|
||||
Ok(())
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use comfy_table::presets;
|
||||
use crossterm::style::Color;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use crate::backend::BackendKind;
|
||||
use crate::{backend::BackendKind, ui::map_color};
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub struct FolderConfig {
|
||||
|
@ -60,8 +62,10 @@ impl FolderAddConfig {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct FolderListConfig {
|
||||
pub backend: Option<BackendKind>,
|
||||
pub table: Option<ListFoldersTableConfig>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub remote: email::folder::list::config::FolderListConfig,
|
||||
|
@ -79,6 +83,28 @@ impl FolderListConfig {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct ListFoldersTableConfig {
|
||||
pub preset: Option<String>,
|
||||
pub name_color: Option<Color>,
|
||||
pub desc_color: Option<Color>,
|
||||
}
|
||||
|
||||
impl ListFoldersTableConfig {
|
||||
pub fn preset(&self) -> &str {
|
||||
self.preset.as_deref().unwrap_or(presets::ASCII_MARKDOWN)
|
||||
}
|
||||
|
||||
pub fn name_color(&self) -> comfy_table::Color {
|
||||
map_color(self.name_color.unwrap_or(Color::Blue))
|
||||
}
|
||||
|
||||
pub fn desc_color(&self) -> comfy_table::Color {
|
||||
map_color(self.desc_color.unwrap_or(Color::Green))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub struct FolderExpungeConfig {
|
||||
pub backend: Option<BackendKind>,
|
||||
|
|
|
@ -2,10 +2,13 @@ pub mod arg;
|
|||
pub mod command;
|
||||
pub mod config;
|
||||
|
||||
use comfy_table::{presets, Attribute, Cell, ContentArrangement, Row, Table};
|
||||
use comfy_table::{Cell, ContentArrangement, Row, Table};
|
||||
use crossterm::style::Color;
|
||||
use serde::{Serialize, Serializer};
|
||||
use std::{fmt, ops::Deref};
|
||||
|
||||
use self::config::ListFoldersTableConfig;
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
pub struct Folder {
|
||||
pub name: String,
|
||||
|
@ -13,11 +16,12 @@ pub struct Folder {
|
|||
}
|
||||
|
||||
impl Folder {
|
||||
pub fn to_row(&self) -> Row {
|
||||
pub fn to_row(&self, config: &ListFoldersTableConfig) -> Row {
|
||||
let mut row = Row::new();
|
||||
row.max_height(1);
|
||||
|
||||
row.add_cell(Cell::new(&self.name).fg(comfy_table::Color::Blue));
|
||||
row.add_cell(Cell::new(&self.desc).fg(comfy_table::Color::Green));
|
||||
row.add_cell(Cell::new(&self.name).fg(config.name_color()));
|
||||
row.add_cell(Cell::new(&self.desc).fg(config.desc_color()));
|
||||
|
||||
row
|
||||
}
|
||||
|
@ -35,23 +39,6 @@ impl From<email::folder::Folder> for Folder {
|
|||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
pub struct Folders(Vec<Folder>);
|
||||
|
||||
impl Folders {
|
||||
pub fn to_table(&self) -> Table {
|
||||
let mut table = Table::new();
|
||||
|
||||
table
|
||||
.load_preset(presets::NOTHING)
|
||||
.set_content_arrangement(ContentArrangement::DynamicFullWidth)
|
||||
.set_header(Row::from([
|
||||
Cell::new("NAME").add_attribute(Attribute::Reverse),
|
||||
Cell::new("DESC").add_attribute(Attribute::Reverse),
|
||||
]))
|
||||
.add_rows(self.iter().map(Folder::to_row));
|
||||
|
||||
table
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Folders {
|
||||
type Target = Vec<Folder>;
|
||||
|
||||
|
@ -69,6 +56,7 @@ impl From<email::folder::Folders> for Folders {
|
|||
pub struct FoldersTable {
|
||||
folders: Folders,
|
||||
width: Option<u16>,
|
||||
config: ListFoldersTableConfig,
|
||||
}
|
||||
|
||||
impl FoldersTable {
|
||||
|
@ -76,6 +64,21 @@ impl FoldersTable {
|
|||
self.width = width;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_preset(mut self, preset: Option<String>) -> Self {
|
||||
self.config.preset = preset;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_name_color(mut self, color: Option<Color>) -> Self {
|
||||
self.config.name_color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_desc_color(mut self, color: Option<Color>) -> Self {
|
||||
self.config.desc_color = color;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Folders> for FoldersTable {
|
||||
|
@ -83,13 +86,24 @@ impl From<Folders> for FoldersTable {
|
|||
Self {
|
||||
folders,
|
||||
width: None,
|
||||
config: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FoldersTable {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut table = self.folders.to_table();
|
||||
let mut table = Table::new();
|
||||
|
||||
table
|
||||
.load_preset(self.config.preset())
|
||||
.set_content_arrangement(ContentArrangement::DynamicFullWidth)
|
||||
.set_header(Row::from([Cell::new("NAME"), Cell::new("DESC")]))
|
||||
.add_rows(
|
||||
self.folders
|
||||
.iter()
|
||||
.map(|folder| folder.to_row(&self.config)),
|
||||
);
|
||||
|
||||
if let Some(width) = self.width {
|
||||
table.set_width(width);
|
||||
|
|
|
@ -1,3 +1,29 @@
|
|||
use crossterm::style::Color;
|
||||
|
||||
pub mod choice;
|
||||
pub mod editor;
|
||||
pub(crate) mod prompt;
|
||||
|
||||
pub(crate) fn map_color(color: Color) -> comfy_table::Color {
|
||||
match color {
|
||||
Color::Reset => comfy_table::Color::Reset,
|
||||
Color::Black => comfy_table::Color::Black,
|
||||
Color::DarkGrey => comfy_table::Color::DarkGrey,
|
||||
Color::Red => comfy_table::Color::Red,
|
||||
Color::DarkRed => comfy_table::Color::DarkRed,
|
||||
Color::Green => comfy_table::Color::Green,
|
||||
Color::DarkGreen => comfy_table::Color::DarkGreen,
|
||||
Color::Yellow => comfy_table::Color::Yellow,
|
||||
Color::DarkYellow => comfy_table::Color::DarkYellow,
|
||||
Color::Blue => comfy_table::Color::Blue,
|
||||
Color::DarkBlue => comfy_table::Color::DarkBlue,
|
||||
Color::Magenta => comfy_table::Color::Magenta,
|
||||
Color::DarkMagenta => comfy_table::Color::DarkMagenta,
|
||||
Color::Cyan => comfy_table::Color::Cyan,
|
||||
Color::DarkCyan => comfy_table::Color::DarkCyan,
|
||||
Color::White => comfy_table::Color::White,
|
||||
Color::Grey => comfy_table::Color::Grey,
|
||||
Color::Rgb { r, g, b } => comfy_table::Color::Rgb { r, g, b },
|
||||
Color::AnsiValue(n) => comfy_table::Color::AnsiValue(n),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue