mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-24 20:10:23 +00:00
add new datetime options
This commit is contained in:
parent
5599a1f5d0
commit
7a6ebc0cd0
8 changed files with 142 additions and 53 deletions
|
@ -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
|
||||
|
||||
|
|
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -1215,7 +1215,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|||
|
||||
[[package]]
|
||||
name = "himalaya"
|
||||
version = "0.8.1-beta"
|
||||
version = "0.8.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"atty",
|
||||
|
@ -2199,9 +2199,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pimalaya-email"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b881982050e29d8f9141a57f0be3f87471394462a2f1695471d5a81f44b4b3a"
|
||||
checksum = "8375dc804686e9e03a8b6f87d80b9038b5a734efb4b77f81b90b07b8ac1d4499"
|
||||
dependencies = [
|
||||
"advisory-lock",
|
||||
"ammonia",
|
||||
|
@ -2244,9 +2244,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pimalaya-email-tpl"
|
||||
version = "0.2.1"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f640b701926112e28b025cea9c9d50a2bfe329862eeb4acbf2d62edeb53fb19b"
|
||||
checksum = "6a838dd91468bf79997ead555d6a5b93f066e363259925ec931d49591b1ebb79"
|
||||
dependencies = [
|
||||
"chumsky 0.9.0",
|
||||
"log",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "himalaya"
|
||||
description = "CLI to manage your emails."
|
||||
version = "0.8.1-beta"
|
||||
version = "0.8.1"
|
||||
authors = ["soywod <clement.douin@posteo.net>"]
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
@ -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()
|
||||
|
|
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
|
|
@ -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>,
|
||||
|
@ -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 {
|
||||
|
@ -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())
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
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.
|
||||
|
@ -13,11 +14,28 @@ use crate::{
|
|||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)?;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue