diff --git a/CHANGELOG.md b/CHANGELOG.md index 2149b72..8afdb04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed config deserialization issue with `email-hooks` and `email-reading-format`. +- Fixed flags case sensitivity. ## [0.7.1] - 2023-02-14 diff --git a/Cargo.lock b/Cargo.lock index 8828de9..b152a64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -775,6 +775,7 @@ version = "0.7.1" dependencies = [ "anyhow", "atty", + "chrono", "clap", "clap_complete", "clap_mangen", @@ -803,7 +804,7 @@ dependencies = [ [[package]] name = "himalaya-lib" version = "0.6.0" -source = "git+https://git.sr.ht/~soywod/himalaya-lib?branch=develop#8c538689c9d3098f8e69493791ccbbe52eb9af58" +source = "git+https://git.sr.ht/~soywod/himalaya-lib?branch=develop#26d3b9e74c978f6dc94689bff809d48fe5ba4c85" dependencies = [ "ammonia", "chrono", @@ -827,7 +828,6 @@ dependencies = [ "regex", "rfc2047-decoder", "rusqlite", - "serde", "shellexpand", "thiserror", "tree_magic", diff --git a/Cargo.toml b/Cargo.toml index 3e5690f..7d47001 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ tempfile = "3.3" [dependencies] anyhow = "1.0" atty = "0.2" +chrono = "0.4.23" clap = "4.0" clap_complete = "4.0" clap_mangen = "0.2" diff --git a/src/domain/email/handlers.rs b/src/domain/email/handlers.rs index 3261a02..c44a35e 100644 --- a/src/domain/email/handlers.rs +++ b/src/domain/email/handlers.rs @@ -14,6 +14,7 @@ use uuid::Uuid; use crate::{ printer::{PrintTableOpts, Printer}, ui::editor, + Envelopes, }; pub fn attachments( @@ -132,10 +133,10 @@ pub fn list( let folder = config.folder_alias(folder)?; let page_size = page_size.unwrap_or(config.email_listing_page_size()); debug!("page size: {}", page_size); - let msgs = backend.list_envelopes(&folder, page_size, page)?; - trace!("envelopes: {:?}", msgs); + let envelopes: Envelopes = backend.list_envelopes(&folder, page_size, page)?.into(); + trace!("envelopes: {:?}", envelopes); printer.print_table( - Box::new(msgs), + Box::new(envelopes), PrintTableOpts { format: &config.email_reading_format, max_width, @@ -291,7 +292,9 @@ pub fn search( ) -> Result<()> { let folder = config.folder_alias(folder)?; let page_size = page_size.unwrap_or(config.email_listing_page_size()); - let envelopes = backend.search_envelopes(&folder, &query, "", page_size, page)?; + let envelopes: Envelopes = backend + .search_envelopes(&folder, &query, "", page_size, page)? + .into(); let opts = PrintTableOpts { format: &config.email_reading_format, max_width, @@ -313,7 +316,9 @@ pub fn sort( ) -> Result<()> { let folder = config.folder_alias(folder)?; let page_size = page_size.unwrap_or(config.email_listing_page_size()); - let envelopes = backend.search_envelopes(&folder, &query, &sort, page_size, page)?; + let envelopes: Envelopes = backend + .search_envelopes(&folder, &query, &sort, page_size, page)? + .into(); let opts = PrintTableOpts { format: &config.email_reading_format, max_width, diff --git a/src/domain/envelope/envelope.rs b/src/domain/envelope/envelope.rs index a62254f..5c24133 100644 --- a/src/domain/envelope/envelope.rs +++ b/src/domain/envelope/envelope.rs @@ -1,6 +1,45 @@ -use himalaya_lib::{Envelope, Flag}; +use chrono::{DateTime, Local}; +use serde::{Serialize, Serializer}; -use crate::ui::{Cell, Row, Table}; +use crate::{ + ui::{Cell, Row, Table}, + Flag, Flags, +}; + +fn date(date: &DateTime, s: S) -> Result { + s.serialize_str(&date.to_rfc3339()) +} + +#[derive(Clone, Debug, Default, Serialize)] +pub struct Mailbox { + pub name: Option, + pub addr: String, +} + +#[derive(Clone, Debug, Default, Serialize)] +pub struct Envelope { + pub id: String, + pub flags: Flags, + pub subject: String, + pub from: Mailbox, + #[serde(serialize_with = "date")] + pub date: DateTime, +} + +impl From<&himalaya_lib::Envelope> for Envelope { + fn from(envelope: &himalaya_lib::Envelope) -> Self { + Envelope { + id: envelope.id.clone(), + flags: envelope.flags.clone().into(), + subject: envelope.subject.clone(), + from: Mailbox { + name: envelope.from.name.clone(), + addr: envelope.from.addr.clone(), + }, + date: envelope.date.clone(), + } + } +} impl Table for Envelope { fn head() -> Row { @@ -14,15 +53,29 @@ impl Table for Envelope { fn row(&self) -> Row { let id = self.id.to_string(); - let flags = self.flags.to_symbols_string(); let unseen = !self.flags.contains(&Flag::Seen); + let flags = { + let mut flags = String::new(); + flags.push_str(if !unseen { " " } else { "✷" }); + flags.push_str(if self.flags.contains(&Flag::Answered) { + "↵" + } else { + " " + }); + flags.push_str(if self.flags.contains(&Flag::Flagged) { + "⚑" + } else { + " " + }); + flags + }; let subject = &self.subject; let sender = if let Some(name) = &self.from.name { name } else { &self.from.addr }; - let date = self.date.format("%d/%m/%Y %H:%M").to_string(); + let date = self.date.to_rfc3339(); Row::new() .cell(Cell::new(id).bold_if(unseen).red()) diff --git a/src/domain/envelope/envelopes.rs b/src/domain/envelope/envelopes.rs index 1bbaa31..1842e8b 100644 --- a/src/domain/envelope/envelopes.rs +++ b/src/domain/envelope/envelopes.rs @@ -1,11 +1,32 @@ +use std::ops; + use anyhow::Result; -use himalaya_lib::Envelopes; +use serde::Serialize; use crate::{ printer::{PrintTable, PrintTableOpts, WriteColor}, ui::Table, + Envelope, }; +/// Represents the list of envelopes. +#[derive(Clone, Debug, Default, Serialize)] +pub struct Envelopes(Vec); + +impl ops::Deref for Envelopes { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for Envelopes { + fn from(envelopes: himalaya_lib::Envelopes) -> Self { + Envelopes(envelopes.iter().map(Envelope::from).collect()) + } +} + impl PrintTable for Envelopes { fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> { writeln!(writer)?; diff --git a/src/domain/flag/args.rs b/src/domain/flag/args.rs index 8c476f8..5a660a0 100644 --- a/src/domain/flag/args.rs +++ b/src/domain/flag/args.rs @@ -83,7 +83,11 @@ pub fn flags_arg() -> Arg { Arg::new(ARG_FLAGS) .value_name("FLAGS") .help("The flags") - .long_help("The list of flags. It can be one of: seen, answered, flagged, deleted, draft, recent. Other flags are considered custom.") + .long_help( + "The list of flags. +It can be one of: seen, answered, flagged, deleted, or draft. +Other flags are considered custom.", + ) .num_args(1..) .required(true) .last(true) diff --git a/src/domain/flag/flag.rs b/src/domain/flag/flag.rs new file mode 100644 index 0000000..d9f2832 --- /dev/null +++ b/src/domain/flag/flag.rs @@ -0,0 +1,25 @@ +use serde::Serialize; + +/// Represents the flag variants. +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)] +pub enum Flag { + Seen, + Answered, + Flagged, + Deleted, + Draft, + Custom(String), +} + +impl From<&himalaya_lib::Flag> for Flag { + fn from(flag: &himalaya_lib::Flag) -> Self { + match flag { + himalaya_lib::Flag::Seen => Flag::Seen, + himalaya_lib::Flag::Answered => Flag::Answered, + himalaya_lib::Flag::Flagged => Flag::Flagged, + himalaya_lib::Flag::Deleted => Flag::Deleted, + himalaya_lib::Flag::Draft => Flag::Draft, + himalaya_lib::Flag::Custom(flag) => Flag::Custom(flag.clone()), + } + } +} diff --git a/src/domain/flag/flags.rs b/src/domain/flag/flags.rs new file mode 100644 index 0000000..b413526 --- /dev/null +++ b/src/domain/flag/flags.rs @@ -0,0 +1,21 @@ +use serde::Serialize; +use std::{collections::HashSet, ops}; + +use crate::Flag; + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)] +pub struct Flags(pub HashSet); + +impl ops::Deref for Flags { + type Target = HashSet; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for Flags { + fn from(flags: himalaya_lib::Flags) -> Self { + Flags(flags.iter().map(Flag::from).collect()) + } +} diff --git a/src/domain/flag/mod.rs b/src/domain/flag/mod.rs index b0b957b..5d92934 100644 --- a/src/domain/flag/mod.rs +++ b/src/domain/flag/mod.rs @@ -1,2 +1,8 @@ pub mod args; pub mod handlers; + +pub mod flag; +pub use flag::*; + +pub mod flags; +pub use flags::*; diff --git a/src/domain/folder/folder.rs b/src/domain/folder/folder.rs index ba0d931..8e201d2 100644 --- a/src/domain/folder/folder.rs +++ b/src/domain/folder/folder.rs @@ -1,7 +1,24 @@ -use himalaya_lib::folder::Folder; +use serde::Serialize; use crate::ui::{Cell, Row, Table}; +#[derive(Clone, Debug, Default, Serialize)] +pub struct Folder { + pub delim: String, + pub name: String, + pub desc: String, +} + +impl From<&himalaya_lib::Folder> for Folder { + fn from(folder: &himalaya_lib::Folder) -> Self { + Folder { + delim: folder.delim.clone(), + name: folder.name.clone(), + desc: folder.desc.clone(), + } + } +} + impl Table for Folder { fn head() -> Row { Row::new() diff --git a/src/domain/folder/folders.rs b/src/domain/folder/folders.rs index db34dad..93426d8 100644 --- a/src/domain/folder/folders.rs +++ b/src/domain/folder/folders.rs @@ -1,11 +1,31 @@ +use std::ops; + use anyhow::Result; -use himalaya_lib::folder::Folders; +use serde::Serialize; use crate::{ printer::{PrintTable, PrintTableOpts, WriteColor}, ui::Table, + Folder, }; +#[derive(Clone, Debug, Default, Serialize)] +pub struct Folders(Vec); + +impl ops::Deref for Folders { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for Folders { + fn from(folders: himalaya_lib::Folders) -> Self { + Folders(folders.iter().map(Folder::from).collect()) + } +} + impl PrintTable for Folders { fn print_table(&self, writer: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> { writeln!(writer)?; diff --git a/src/domain/folder/handlers.rs b/src/domain/folder/handlers.rs index 85c1516..3fd8e9b 100644 --- a/src/domain/folder/handlers.rs +++ b/src/domain/folder/handlers.rs @@ -5,7 +5,10 @@ use anyhow::Result; use himalaya_lib::{AccountConfig, Backend}; -use crate::printer::{PrintTableOpts, Printer}; +use crate::{ + printer::{PrintTableOpts, Printer}, + Folders, +}; pub fn expunge( folder: &str, @@ -22,7 +25,7 @@ pub fn list( printer: &mut P, backend: &mut B, ) -> Result<()> { - let folders = backend.list_folders()?; + let folders: Folders = backend.list_folders()?.into(); printer.print_table( // TODO: remove Box Box::new(folders),