diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d9e157..d095b72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Vim table containing emoji [#122] - IDLE mode after network interruption [#123] - Output redirected to `stderr` [#130] +- Refactor table system [#132] ## [0.2.6] - 2021-04-17 @@ -209,3 +210,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#125]: https://github.com/soywod/himalaya/issues/125 [#126]: https://github.com/soywod/himalaya/issues/126 [#130]: https://github.com/soywod/himalaya/issues/130 +[#132]: https://github.com/soywod/himalaya/issues/132 diff --git a/Cargo.toml b/Cargo.toml index aa9174f..2266dc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "himalaya" -description = "πŸ“« The CLI email client." +description = "πŸ“« CLI email client" version = "0.2.7" authors = ["soywod "] edition = "2018" @@ -16,10 +16,10 @@ log = "0.4.14" mailparse = "0.13.1" 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" terminal_size = "0.1.15" toml = "0.5.8" tree_magic = "0.2.3" unicode-width = "0.1.7" -uuid = { version = "0.8", features = ["v4"] } +uuid = {version = "0.8", features = ["v4"]} diff --git a/README.md b/README.md index f771bbd..c1908cc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # πŸ“« Himalaya [![gh-actions](https://github.com/soywod/himalaya/workflows/deployment/badge.svg)](https://github.com/soywod/himalaya/actions?query=workflow%3Adeployment) -The CLI email client. +CLI email client written in Rust. *The project is under active development. Do not use in production before the `v1.0.0` (see the [roadmap](https://github.com/soywod/himalaya/milestone/5)).* diff --git a/flake.nix b/flake.nix index f690431..ab090c2 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "The CLI email client."; + description = "πŸ“« CLI email client"; inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; diff --git a/src/flag/model.rs b/src/flag/model.rs index 5980859..74d0eb4 100644 --- a/src/flag/model.rs +++ b/src/flag/model.rs @@ -43,11 +43,11 @@ impl<'f> ToString for Flags<'f> { flags.push_str(if self.0.contains(&Flag::Seen) { " " } else { - "πŸŸ“" + "✷" }); flags.push_str(if self.0.contains(&Flag::Answered) { - "↩" + "↡" } else { " " }); diff --git a/src/mbox/model.rs b/src/mbox/model.rs index b12844d..7e1db6d 100644 --- a/src/mbox/model.rs +++ b/src/mbox/model.rs @@ -4,7 +4,7 @@ use std::fmt; use crate::{ output::fmt::{get_output_fmt, OutputFmt, Response}, - table::{self, DisplayRow, DisplayTable}, + table::{Cell, Row, Table}, }; // Mbox @@ -26,15 +26,25 @@ impl Mbox { } } -impl DisplayRow for Mbox { - fn to_row(&self) -> Vec { - use crate::table::*; +impl Table for Mbox { + fn head() -> Row { + Row::new() + .cell(Cell::new("DELIM").bold().underline().white()) + .cell(Cell::new("NAME").bold().underline().white()) + .cell( + Cell::new("ATTRIBUTES") + .shrinkable() + .bold() + .underline() + .white(), + ) + } - vec![ - Cell::new(&[BLUE], &self.delim), - Cell::new(&[GREEN], &self.name), - FlexCell::new(&[YELLOW], &self.attributes.join(", ")), - ] + fn row(&self) -> Row { + Row::new() + .cell(Cell::new(&self.delim).red()) + .cell(Cell::new(&self.name).green()) + .cell(Cell::new(&self.attributes.join(", ")).shrinkable().yellow()) } } @@ -43,28 +53,12 @@ impl DisplayRow for Mbox { #[derive(Debug, Serialize)] pub struct Mboxes(pub Vec); -impl<'a> DisplayTable<'a, Mbox> for Mboxes { - fn header_row() -> Vec { - use crate::table::*; - - vec![ - Cell::new(&[BOLD, UNDERLINE, WHITE], "DELIM"), - Cell::new(&[BOLD, UNDERLINE, WHITE], "NAME"), - FlexCell::new(&[BOLD, UNDERLINE, WHITE], "ATTRIBUTES"), - ] - } - - fn rows(&self) -> &Vec { - &self.0 - } -} - impl fmt::Display for Mboxes { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { unsafe { match get_output_fmt() { &OutputFmt::Plain => { - writeln!(f, "\n{}", self.to_table()) + writeln!(f, "\n{}", Table::render(&self.0)) } &OutputFmt::Json => { let res = serde_json::to_string(&Response::new(self)).unwrap(); diff --git a/src/msg/model.rs b/src/msg/model.rs index a6527e2..72c72dc 100644 --- a/src/msg/model.rs +++ b/src/msg/model.rs @@ -16,7 +16,7 @@ use crate::{ config::model::{Account, Config}, flag::model::{Flag, Flags}, output::fmt::{get_output_fmt, OutputFmt, Response}, - table::{self, DisplayRow, DisplayTable}, + table::{Cell, Row, Table}, }; error_chain! { @@ -624,23 +624,29 @@ struct MsgSpec { default_content: Option>, } -impl<'m> DisplayRow for Msg<'m> { - fn to_row(&self) -> Vec { - use crate::table::*; +impl<'m> Table for Msg<'m> { + fn head() -> Row { + Row::new() + .cell(Cell::new("UID").bold().underline().white()) + .cell(Cell::new("FLAGS").bold().underline().white()) + .cell(Cell::new("SUBJECT").shrinkable().bold().underline().white()) + .cell(Cell::new("SENDER").bold().underline().white()) + .cell(Cell::new("DATE").bold().underline().white()) + } - let unseen = if self.flags.contains(&Flag::Seen) { - RESET - } else { - BOLD - }; - - vec![ - Cell::new(&[unseen.to_owned(), RED], &self.uid.to_string()), - Cell::new(&[unseen.to_owned(), WHITE], &self.flags.to_string()), - FlexCell::new(&[unseen.to_owned(), GREEN], &self.subject), - Cell::new(&[unseen.to_owned(), BLUE], &self.sender), - Cell::new(&[unseen.to_owned(), YELLOW], &self.date), - ] + fn row(&self) -> Row { + let is_seen = !self.flags.contains(&Flag::Seen); + Row::new() + .cell(Cell::new(&self.uid.to_string()).bold_if(is_seen).red()) + .cell(Cell::new(&self.flags.to_string()).bold_if(is_seen).white()) + .cell( + Cell::new(&self.subject) + .shrinkable() + .bold_if(is_seen) + .green(), + ) + .cell(Cell::new(&self.sender).bold_if(is_seen).blue()) + .cell(Cell::new(&self.date).bold_if(is_seen).yellow()) } } @@ -649,24 +655,6 @@ impl<'m> DisplayRow for Msg<'m> { #[derive(Debug, Serialize)] pub struct Msgs<'m>(pub Vec>); -impl<'m> DisplayTable<'m, Msg<'m>> for Msgs<'m> { - fn header_row() -> Vec { - use crate::table::*; - - vec![ - Cell::new(&[BOLD, UNDERLINE, WHITE], "UID"), - Cell::new(&[BOLD, UNDERLINE, WHITE], "FLAGS"), - FlexCell::new(&[BOLD, UNDERLINE, WHITE], "SUBJECT"), - Cell::new(&[BOLD, UNDERLINE, WHITE], "SENDER"), - Cell::new(&[BOLD, UNDERLINE, WHITE], "DATE"), - ] - } - - fn rows(&self) -> &Vec> { - &self.0 - } -} - impl<'m> From<&'m imap::types::ZeroCopy>> for Msgs<'m> { fn from(fetches: &'m imap::types::ZeroCopy>) -> Self { Self(fetches.iter().rev().map(Msg::from).collect::>()) @@ -678,7 +666,7 @@ impl<'m> fmt::Display for Msgs<'m> { unsafe { match get_output_fmt() { &OutputFmt::Plain => { - writeln!(f, "\n{}", self.to_table()) + writeln!(f, "\n{}", Table::render(&self.0)) } &OutputFmt::Json => { let res = serde_json::to_string(&Response::new(self)).unwrap(); diff --git a/src/table.rs b/src/table.rs index 4f8a911..9859f5f 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1,30 +1,26 @@ use std::fmt; -use terminal_size::terminal_size; use unicode_width::UnicodeWidthStr; -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct Style(u8, u8, u8); impl fmt::Display for Style { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Style(color, bright, shade) = self; - - let bright_str: String = if *bright > 0 { - String::from(";") + &bright.to_string() - } else { - String::from("") - }; - - let shade_str: String = if *shade > 0 { - String::from(";") + &shade.to_string() - } else { - String::from("") - }; - let mut style = String::from("\x1b["); + style.push_str(&color.to_string()); - style.push_str(&bright_str); - style.push_str(&shade_str); + + if *bright > 0 { + style.push_str(";"); + style.push_str(&bright.to_string()); + }; + + if *shade > 0 { + style.push_str(";"); + style.push_str(&shade.to_string()); + }; + style.push_str("m"); write!(f, "{}", style) @@ -33,17 +29,17 @@ impl fmt::Display for Style { #[derive(Debug)] pub struct Cell { - pub styles: Vec