Skip to content

Commit

Permalink
add new datetime options
Browse files Browse the repository at this point in the history
  • Loading branch information
soywod committed Jun 15, 2023
1 parent 5599a1f commit 7a6ebc0
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 53 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Implemented OAuth 2.0 refresh token flow for IMAP and SMTP, which means that access tokens are now automatically refreshed and is transparent for users.
- Added `imap-oauth2-redirect-host` and `smtp-oauth2-redirect-host` options to customize the redirect server host name (default: `localhost`).
- Added `imap-oauth2-redirect-port` and `smtp-oauth2-redirect-port` options to customize the redirect server port (default: `9999`).
- Added `email-listing-datetime-fmt` to customize envelopes datetime format. See format spec at <https://docs.rs/chrono/latest/chrono/format/strftime/index.html>.
- Added `email-listing-local-datetime` to transform envelopes datetime's timezone to the user's local one. For example, if the user's local is set to `UTC`, the envelope date `2023-06-15T09:00:00+02:00` becomes `2023-06-15T07:00:00-00:00`.

### Fixed

- Fixed missing `<` and `>` around `Message-ID` and `In-Reply-To` headers.

## [0.8.0] - 2023-06-03

Expand Down
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "himalaya"
description = "CLI to manage your emails."
version = "0.8.1-beta"
version = "0.8.1"
authors = ["soywod <[email protected]>"]
edition = "2021"
license = "MIT"
Expand Down Expand Up @@ -44,7 +44,7 @@ indicatif = "0.17"
log = "0.4"
md5 = "0.7.0"
once_cell = "1.16.0"
pimalaya-email = { version = "=0.10.0", default-features = false }
pimalaya-email = { version = "=0.11.0", default-features = false }
pimalaya-keyring = "=0.0.4"
pimalaya-oauth2 = "=0.0.3"
pimalaya-process = "=0.0.2"
Expand Down
2 changes: 2 additions & 0 deletions src/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ pub struct DeserializedConfig {
pub folder_aliases: Option<HashMap<String, String>>,

pub email_listing_page_size: Option<usize>,
pub email_listing_datetime_fmt: Option<String>,
pub email_listing_datetime_local_tz: Option<bool>,
pub email_reading_headers: Option<Vec<String>>,
#[serde(
default,
Expand Down
15 changes: 15 additions & 0 deletions src/domain/account/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pub struct DeserializedAccountConfig {
pub folder_aliases: Option<HashMap<String, String>>,

pub email_listing_page_size: Option<usize>,
pub email_listing_datetime_fmt: Option<String>,
pub email_listing_datetime_local_tz: Option<bool>,
pub email_reading_headers: Option<Vec<String>>,
#[serde(
default,
Expand Down Expand Up @@ -131,6 +133,19 @@ impl DeserializedAccountConfig {
email_listing_page_size: self
.email_listing_page_size
.or_else(|| config.email_listing_page_size),
email_listing_datetime_fmt: self
.email_listing_datetime_fmt
.as_ref()
.map(ToOwned::to_owned)
.or_else(|| {
config
.email_listing_datetime_fmt
.as_ref()
.map(ToOwned::to_owned)
}),
email_listing_datetime_local_tz: self
.email_listing_datetime_local_tz
.or_else(|| config.email_listing_datetime_local_tz),
email_reading_headers: self
.email_reading_headers
.as_ref()
Expand Down
25 changes: 15 additions & 10 deletions src/domain/email/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,11 @@ pub fn list<P: Printer>(
let page_size = page_size.unwrap_or(config.email_listing_page_size());
debug!("page size: {}", page_size);

let mut envelopes: Envelopes = backend.list_envelopes(&folder, page_size, page)?.into();
envelopes.remap_ids(id_mapper)?;
let envelopes = Envelopes::from_backend(
config,
id_mapper,
backend.list_envelopes(&folder, page_size, page)?,
)?;
trace!("envelopes: {:?}", envelopes);

printer.print_table(
Expand Down Expand Up @@ -326,10 +329,11 @@ pub fn search<P: Printer>(
) -> Result<()> {
let folder = config.folder_alias(folder)?;
let page_size = page_size.unwrap_or(config.email_listing_page_size());
let mut envelopes: Envelopes = backend
.search_envelopes(&folder, &query, "", page_size, page)?
.into();
envelopes.remap_ids(id_mapper)?;
let envelopes = Envelopes::from_backend(
config,
id_mapper,
backend.search_envelopes(&folder, &query, "", page_size, page)?,
)?;
let opts = PrintTableOpts {
format: &config.email_reading_format,
max_width,
Expand All @@ -352,10 +356,11 @@ pub fn sort<P: Printer>(
) -> Result<()> {
let folder = config.folder_alias(folder)?;
let page_size = page_size.unwrap_or(config.email_listing_page_size());
let mut envelopes: Envelopes = backend
.search_envelopes(&folder, &query, &sort, page_size, page)?
.into();
envelopes.remap_ids(id_mapper)?;
let envelopes = Envelopes::from_backend(
config,
id_mapper,
backend.search_envelopes(&folder, &query, &sort, page_size, page)?,
)?;
let opts = PrintTableOpts {
format: &config.email_reading_format,
max_width,
Expand Down
27 changes: 3 additions & 24 deletions src/domain/envelope/envelope.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
use chrono::{DateTime, Local};
use serde::{Serialize, Serializer};
use serde::Serialize;

use crate::{
ui::{Cell, Row, Table},
Flag, Flags,
};

fn date<S: Serializer>(date: &DateTime<Local>, s: S) -> Result<S::Ok, S::Error> {
s.serialize_str(&date.to_rfc3339())
}

#[derive(Clone, Debug, Default, Serialize)]
pub struct Mailbox {
pub name: Option<String>,
Expand All @@ -22,23 +17,7 @@ pub struct Envelope {
pub flags: Flags,
pub subject: String,
pub from: Mailbox,
#[serde(serialize_with = "date")]
pub date: DateTime<Local>,
}

impl From<&pimalaya_email::Envelope> for Envelope {
fn from(envelope: &pimalaya_email::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(),
}
}
pub date: String,
}

impl Table for Envelope {
Expand Down Expand Up @@ -75,7 +54,7 @@ impl Table for Envelope {
} else {
&self.from.addr
};
let date = self.date.to_rfc3339();
let date = &self.date;

Row::new()
.cell(Cell::new(id).bold_if(unseen).red())
Expand Down
106 changes: 94 additions & 12 deletions src/domain/envelope/envelopes.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,41 @@
use anyhow::Result;
use pimalaya_email::AccountConfig;
use serde::Serialize;
use std::ops;

use crate::{
printer::{PrintTable, PrintTableOpts, WriteColor},
ui::Table,
Envelope, IdMapper,
Envelope, IdMapper, Mailbox,
};

/// Represents the list of envelopes.
#[derive(Clone, Debug, Default, Serialize)]
pub struct Envelopes(Vec<Envelope>);

impl Envelopes {
pub fn remap_ids(&mut self, id_mapper: &IdMapper) -> Result<()> {
for envelope in &mut self.0 {
envelope.id = id_mapper.get_or_create_alias(&envelope.id)?;
}
Ok(())
pub fn from_backend(
config: &AccountConfig,
id_mapper: &IdMapper,
envelopes: pimalaya_email::Envelopes,
) -> Result<Envelopes> {
let envelopes = envelopes
.iter()
.map(|envelope| {
Ok(Envelope {
id: id_mapper.get_or_create_alias(&envelope.id)?,
flags: envelope.flags.clone().into(),
subject: envelope.subject.clone(),
from: Mailbox {
name: envelope.from.name.clone(),
addr: envelope.from.addr.clone(),
},
date: envelope.format_date(config),
})
})
.collect::<Result<Vec<_>>>()?;

Ok(Envelopes(envelopes))
}
}

Expand All @@ -29,12 +47,6 @@ impl ops::Deref for Envelopes {
}
}

impl From<pimalaya_email::Envelopes> for Envelopes {
fn from(envelopes: pimalaya_email::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)?;
Expand All @@ -43,3 +55,73 @@ impl PrintTable for Envelopes {
Ok(())
}
}

#[cfg(test)]
mod tests {
use std::env;

use chrono::DateTime;
use pimalaya_email::AccountConfig;

use crate::{Envelopes, IdMapper};

#[test]
fn default_datetime_fmt() {
let config = AccountConfig::default();
let id_mapper = IdMapper::Dummy;

let envelopes = pimalaya_email::Envelopes::from_iter([pimalaya_email::Envelope {
date: DateTime::parse_from_rfc3339("2023-06-15T09:42:00+04:00").unwrap(),
..Default::default()
}]);
let envelopes = Envelopes::from_backend(&config, &id_mapper, envelopes).unwrap();

let expected_date = "2023-06-15 09:42+04:00";
let date = &envelopes.first().unwrap().date;

assert_eq!(date, expected_date);
}

#[test]
fn custom_datetime_fmt() {
let id_mapper = IdMapper::Dummy;
let config = AccountConfig {
email_listing_datetime_fmt: Some("%d/%m/%Y %Hh%M".into()),
..AccountConfig::default()
};

let envelopes = pimalaya_email::Envelopes::from_iter([pimalaya_email::Envelope {
date: DateTime::parse_from_rfc3339("2023-06-15T09:42:00+04:00").unwrap(),
..Default::default()
}]);
let envelopes = Envelopes::from_backend(&config, &id_mapper, envelopes).unwrap();

let expected_date = "15/06/2023 09h42";
let date = &envelopes.first().unwrap().date;

assert_eq!(date, expected_date);
}

#[test]
fn custom_datetime_fmt_with_local_tz() {
env::set_var("TZ", "UTC");

let id_mapper = IdMapper::Dummy;
let config = AccountConfig {
email_listing_datetime_fmt: Some("%d/%m/%Y %Hh%M".into()),
email_listing_datetime_local_tz: Some(true),
..AccountConfig::default()
};

let envelopes = pimalaya_email::Envelopes::from_iter([pimalaya_email::Envelope {
date: DateTime::parse_from_rfc3339("2023-06-15T09:42:00+04:00").unwrap(),
..Default::default()
}]);
let envelopes = Envelopes::from_backend(&config, &id_mapper, envelopes).unwrap();

let expected_date = "15/06/2023 05h42";
let date = &envelopes.first().unwrap().date;

assert_eq!(date, expected_date);
}
}

0 comments on commit 7a6ebc0

Please sign in to comment.