From b855c445087c082880e06d0df73e30459e4df538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Fri, 25 Feb 2022 21:56:48 +0100 Subject: [PATCH] replace `xxx-folder` config props by `mailboxes` --- CHANGELOG.md | 4 +++ src/backends/maildir/maildir_backend.rs | 11 +++++++-- src/config/account_config.rs | 30 ++++------------------- src/config/deserialized_account_config.rs | 17 ++++++------- src/config/deserialized_config.rs | 10 ++------ src/main.rs | 8 ++++-- src/msg/msg_entity.rs | 21 ++++++++++------ src/msg/msg_handler.rs | 12 ++++++--- 8 files changed, 56 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8acd964..0ec8fda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - No tilde expansion in `maildir-dir` [#305] - Unknown command SORT [#308] +### Changed + +- [**BREAKING**] Replace `inbox-folder`, `sent-folder` and `draft-folder` by a generic hashmap `mailboxes` + ## [0.5.6] - 2022-02-22 ### Added diff --git a/src/backends/maildir/maildir_backend.rs b/src/backends/maildir/maildir_backend.rs index 44618f2..19e3812 100644 --- a/src/backends/maildir/maildir_backend.rs +++ b/src/backends/maildir/maildir_backend.rs @@ -3,7 +3,7 @@ use std::{convert::TryInto, fs, path::PathBuf}; use crate::{ backends::{Backend, MaildirEnvelopes, MaildirFlags, MaildirMboxes}, - config::{AccountConfig, MaildirBackendConfig}, + config::{AccountConfig, MaildirBackendConfig, DEFAULT_INBOX_FOLDER}, mbox::Mboxes, msg::{Envelopes, Msg}, }; @@ -36,7 +36,14 @@ impl<'a> MaildirBackend<'a> { } fn get_mdir_from_name(&self, mdir: &str) -> Result { - if mdir == self.account_config.inbox_folder { + let inbox_folder = self + .account_config + .mailboxes + .get("inbox") + .map(|s| s.as_str()) + .unwrap_or(DEFAULT_INBOX_FOLDER); + + if mdir == inbox_folder { self.validate_mdir_path(self.mdir.path().to_owned()) .map(maildir::Maildir::from) } else { diff --git a/src/config/account_config.rs b/src/config/account_config.rs index 81c1fa6..b222abd 100644 --- a/src/config/account_config.rs +++ b/src/config/account_config.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Context, Result}; use lettre::transport::smtp::authentication::Credentials as SmtpCredentials; use log::{debug, info, trace}; use mailparse::MailAddr; -use std::{env, ffi::OsStr, fs, path::PathBuf}; +use std::{collections::HashMap, env, ffi::OsStr, fs, path::PathBuf}; use crate::{config::*, output::run_cmd}; @@ -23,12 +23,6 @@ pub struct AccountConfig { pub sig: Option, /// Represents the default page size for listings. pub default_page_size: usize, - /// Represents the inbox folder name for this account. - pub inbox_folder: String, - /// Represents the sent folder name for this account. - pub sent_folder: String, - /// Represents the draft folder name for this account. - pub draft_folder: String, /// Represents the notify command. pub notify_cmd: Option, /// Overrides the default IMAP query "NEW" used to fetch new messages @@ -36,6 +30,9 @@ pub struct AccountConfig { /// Represents the watch commands. pub watch_cmds: Vec, + /// Represents mailbox aliases. + pub mailboxes: HashMap, + /// Represents the SMTP host. pub smtp_host: String, /// Represents the SMTP port. @@ -137,24 +134,6 @@ impl<'a> AccountConfig { downloads_dir, sig, default_page_size, - inbox_folder: base_account - .inbox_folder - .as_deref() - .or_else(|| config.inbox_folder.as_deref()) - .unwrap_or(DEFAULT_INBOX_FOLDER) - .to_string(), - sent_folder: base_account - .sent_folder - .as_deref() - .or_else(|| config.sent_folder.as_deref()) - .unwrap_or(DEFAULT_SENT_FOLDER) - .to_string(), - draft_folder: base_account - .draft_folder - .as_deref() - .or_else(|| config.draft_folder.as_deref()) - .unwrap_or(DEFAULT_DRAFT_FOLDER) - .to_string(), notify_cmd: base_account.notify_cmd.clone(), notify_query: base_account .notify_query @@ -168,6 +147,7 @@ impl<'a> AccountConfig { .or_else(|| config.watch_cmds.as_ref()) .unwrap_or(&vec![]) .to_owned(), + mailboxes: base_account.mailboxes.clone(), default: base_account.default.unwrap_or_default(), email: base_account.email.to_owned(), diff --git a/src/config/deserialized_account_config.rs b/src/config/deserialized_account_config.rs index 024cfca..4f5c440 100644 --- a/src/config/deserialized_account_config.rs +++ b/src/config/deserialized_account_config.rs @@ -1,5 +1,5 @@ use serde::Deserialize; -use std::path::PathBuf; +use std::{collections::HashMap, path::PathBuf}; pub trait ToDeserializedBaseAccountConfig { fn to_base(&self) -> DeserializedBaseAccountConfig; @@ -39,12 +39,6 @@ macro_rules! make_account_config { pub signature_delimiter: Option, /// Overrides the default page size for this account. pub default_page_size: Option, - /// Overrides the inbox folder name for this account. - pub inbox_folder: Option, - /// Overrides the sent folder name for this account. - pub sent_folder: Option, - /// Overrides the draft folder name for this account. - pub draft_folder: Option, /// Overrides the notify command for this account. pub notify_cmd: Option, /// Overrides the IMAP query used to fetch new messages for this account. @@ -75,6 +69,10 @@ macro_rules! make_account_config { /// Represents the command used to decrypt a message. pub pgp_decrypt_cmd: Option, + /// Represents mailbox aliases. + #[serde(default)] + pub mailboxes: HashMap, + $(pub $element: $ty),* } @@ -86,9 +84,6 @@ macro_rules! make_account_config { signature: self.signature.clone(), signature_delimiter: self.signature_delimiter.clone(), default_page_size: self.default_page_size.clone(), - inbox_folder: self.inbox_folder.clone(), - sent_folder: self.sent_folder.clone(), - draft_folder: self.draft_folder.clone(), notify_cmd: self.notify_cmd.clone(), notify_query: self.notify_query.clone(), watch_cmds: self.watch_cmds.clone(), @@ -105,6 +100,8 @@ macro_rules! make_account_config { pgp_encrypt_cmd: self.pgp_encrypt_cmd.clone(), pgp_decrypt_cmd: self.pgp_decrypt_cmd.clone(), + + mailboxes: self.mailboxes.clone(), } } } diff --git a/src/config/deserialized_config.rs b/src/config/deserialized_config.rs index 280b6fb..7067162 100644 --- a/src/config/deserialized_config.rs +++ b/src/config/deserialized_config.rs @@ -27,12 +27,6 @@ pub struct DeserializedConfig { pub signature_delimiter: Option, /// Represents the default page size for listings. pub default_page_size: Option, - /// Overrides the default inbox folder name "INBOX". - pub inbox_folder: Option, - /// Overrides the default sent folder name "Sent". - pub sent_folder: Option, - /// Overrides the default draft folder name "Drafts". - pub draft_folder: Option, /// Represents the notify command. pub notify_cmd: Option, /// Overrides the default IMAP query "NEW" used to fetch new messages @@ -48,12 +42,12 @@ pub struct DeserializedConfig { impl DeserializedConfig { /// Tries to create a config from an optional path. pub fn from_opt_path(path: Option<&str>) -> Result { - info!("begin: trying to parse config from path"); + info!("begin: try to parse config from path"); debug!("path: {:?}", path); let path = path.map(|s| s.into()).unwrap_or(Self::path()?); let content = fs::read_to_string(path).context("cannot read config file")?; let config = toml::from_str(&content).context("cannot parse config file")?; - info!("end: trying to parse config from path"); + info!("end: try to parse config from path"); trace!("config: {:?}", config); Ok(config) } diff --git a/src/main.rs b/src/main.rs index e880ee3..f4db6b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,10 @@ use url::Url; use himalaya::{ backends::{imap_arg, imap_handler, Backend, ImapBackend, MaildirBackend, NotmuchBackend}, compl::{compl_arg, compl_handler}, - config::{account_args, config_args, AccountConfig, BackendConfig, DeserializedConfig}, + config::{ + account_args, config_args, AccountConfig, BackendConfig, DeserializedConfig, + DEFAULT_INBOX_FOLDER, + }, mbox::{mbox_arg, mbox_handler}, msg::{flag_arg, flag_handler, msg_arg, msg_handler, tpl_arg, tpl_handler}, output::{output_arg, OutputFmt, StdoutPrinter}, @@ -82,7 +85,8 @@ fn main() -> Result<()> { AccountConfig::from_config_and_opt_account_name(&config, m.value_of("account"))?; let mbox = m .value_of("mbox-source") - .unwrap_or(&account_config.inbox_folder); + .or_else(|| account_config.mailboxes.get("inbox").map(|s| s.as_str())) + .unwrap_or(DEFAULT_INBOX_FOLDER); let mut printer = StdoutPrinter::try_from(m.value_of("output"))?; let mut imap; let mut maildir; diff --git a/src/msg/msg_entity.rs b/src/msg/msg_entity.rs index 2ca5441..3fa4483 100644 --- a/src/msg/msg_entity.rs +++ b/src/msg/msg_entity.rs @@ -10,7 +10,7 @@ use uuid::Uuid; use crate::{ backends::Backend, - config::{AccountConfig, DEFAULT_SIG_DELIM}, + config::{AccountConfig, DEFAULT_DRAFT_FOLDER, DEFAULT_SENT_FOLDER, DEFAULT_SIG_DELIM}, msg::{ from_addrs_to_sendable_addrs, from_addrs_to_sendable_mbox, from_slice_to_addrs, msg_utils, Addrs, BinaryPart, Part, Parts, TextPlainPart, TplOverride, @@ -340,7 +340,12 @@ impl Msg { match choice::post_edit() { Ok(PostEditChoice::Send) => { let sent_msg = smtp.send_msg(account, &self)?; - backend.add_msg(&account.sent_folder, &sent_msg.formatted(), "seen")?; + let sent_folder = account + .mailboxes + .get("sent") + .map(|s| s.as_str()) + .unwrap_or(DEFAULT_SENT_FOLDER); + backend.add_msg(&sent_folder, &sent_msg.formatted(), "seen")?; msg_utils::remove_local_draft()?; printer.print("Message successfully sent")?; break; @@ -355,12 +360,14 @@ impl Msg { } Ok(PostEditChoice::RemoteDraft) => { let tpl = self.to_tpl(TplOverride::default(), account)?; - backend.add_msg(&account.draft_folder, tpl.as_bytes(), "seen draft")?; + let draft_folder = account + .mailboxes + .get("draft") + .map(|s| s.as_str()) + .unwrap_or(DEFAULT_DRAFT_FOLDER); + backend.add_msg(&draft_folder, tpl.as_bytes(), "seen draft")?; msg_utils::remove_local_draft()?; - printer.print(format!( - "Message successfully saved to {}", - account.draft_folder - ))?; + printer.print(format!("Message successfully saved to {}", draft_folder))?; break; } Ok(PostEditChoice::Discard) => { diff --git a/src/msg/msg_handler.rs b/src/msg/msg_handler.rs index 7cd093d..300bcff 100644 --- a/src/msg/msg_handler.rs +++ b/src/msg/msg_handler.rs @@ -16,7 +16,7 @@ use url::Url; use crate::{ backends::Backend, - config::AccountConfig, + config::{AccountConfig, DEFAULT_SENT_FOLDER}, msg::{Msg, Part, Parts, TextPlainPart}, output::{PrintTableOpts, PrinterService}, smtp::SmtpService, @@ -312,6 +312,13 @@ pub fn send<'a, P: PrinterService, B: Backend<'a> + ?Sized, S: SmtpService>( let is_json = printer.is_json(); debug!("is json: {}", is_json); + let sent_folder = config + .mailboxes + .get("sent") + .map(|s| s.as_str()) + .unwrap_or(DEFAULT_SENT_FOLDER); + debug!("sent folder: {:?}", sent_folder); + let raw_msg = if is_tty || is_json { raw_msg.replace("\r", "").replace("\n", "\r\n") } else { @@ -325,9 +332,8 @@ pub fn send<'a, P: PrinterService, B: Backend<'a> + ?Sized, S: SmtpService>( trace!("raw message: {:?}", raw_msg); let envelope: lettre::address::Envelope = Msg::from_tpl(&raw_msg)?.try_into()?; trace!("envelope: {:?}", envelope); - smtp.send_raw_msg(&envelope, raw_msg.as_bytes())?; - backend.add_msg(&config.sent_folder, raw_msg.as_bytes(), "seen")?; + backend.add_msg(&sent_folder, raw_msg.as_bytes(), "seen")?; Ok(()) }