From f06beb61ae9a77691968bfdd7b2c5d67c2c3acf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Fri, 4 Mar 2022 14:19:54 +0100 Subject: [PATCH] make one cargo feature per backend (#318) --- .github/workflows/tests.yaml | 1 + CHANGELOG.md | 2 + Cargo.lock | 10 ++-- Cargo.toml | 19 ++++-- flake.nix | 1 + src/config/account.rs | 6 +- src/config/account_config.rs | 17 ++++-- src/config/account_handlers.rs | 5 +- src/config/deserialized_account_config.rs | 12 +++- src/lib.rs | 13 ++++- src/main.rs | 48 +++++++++++---- src/msg/addr_entity.rs | 71 +---------------------- tests/test_imap_backend.rs | 2 + tests/test_notmuch_backend.rs | 6 +- 14 files changed, 103 insertions(+), 110 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 4692490..e1bffcb 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -33,3 +33,4 @@ jobs: uses: actions-rs/cargo@v1 with: command: test + args: --all-features diff --git a/CHANGELOG.md b/CHANGELOG.md index f64457f..85e7ae5 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 - Flowed format support [#206] - List accounts command [#244] +- One cargo feature per backend [#318] ### Changed @@ -474,4 +475,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#305]: https://github.com/soywod/himalaya/issues/305 [#308]: https://github.com/soywod/himalaya/issues/308 [#309]: https://github.com/soywod/himalaya/issues/309 +[#318]: https://github.com/soywod/himalaya/issues/318 [#321]: https://github.com/soywod/himalaya/issues/321 diff --git a/Cargo.lock b/Cargo.lock index c51aa6c..72d643a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -602,7 +602,7 @@ dependencies = [ "idna", "mime", "native-tls", - "nom 7.0.0", + "nom 7.1.0", "once_cell", "quoted_printable", "regex", @@ -742,9 +742,9 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "minimal-lexical" -version = "0.1.4" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c64630dcdd71f1a64c435f54885086a0de5d6a12d104d69b165fb7d5286d677" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "native-tls" @@ -793,9 +793,9 @@ dependencies = [ [[package]] name = "nom" -version = "7.0.0" +version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" dependencies = [ "memchr 2.4.1", "minimal-lexical", diff --git a/Cargo.toml b/Cargo.toml index ee3f0a3..735754a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,12 @@ repository = "https://github.com/soywod/himalaya" priority = "optional" section = "mail" +[features] +imap-backend = ["imap", "imap-proto"] +maildir-backend = ["maildir", "md5"] +notmuch-backend = ["notmuch", "maildir-backend"] +default = ["imap-backend", "maildir-backend"] + [dependencies] ammonia = "3.1.2" anyhow = "1.0.44" @@ -25,15 +31,10 @@ clap = { version = "2.33.3", default-features = false, features = ["suggestions" env_logger = "0.8.3" erased-serde = "0.3.18" html-escape = "0.2.9" -imap = "=3.0.0-alpha.4" -imap-proto = "0.14.3" lettre = { version = "0.10.0-rc.1", features = ["serde"] } log = "0.4.14" -maildir = "0.6.0" mailparse = "0.13.6" -md5 = "0.7.0" native-tls = "0.2.8" -notmuch = { version = "0.7.1", optional = true } regex = "1.5.4" rfc2047-decoder = "0.1.2" serde = { version = "1.0.118", features = ["derive"] } @@ -46,3 +47,11 @@ tree_magic = "0.2.3" unicode-width = "0.1.7" url = "2.2.2" uuid = { version = "0.8", features = ["v4"] } + +# Optional dependencies: + +imap = { version = "=3.0.0-alpha.4", optional = true } +imap-proto = { version = "0.14.3", optional = true } +maildir = { version = "0.6.0", optional = true } +md5 = { version = "0.7.0", optional = true } +notmuch = { version = "0.7.1", optional = true } diff --git a/flake.nix b/flake.nix index a63b92f..596f4b2 100644 --- a/flake.nix +++ b/flake.nix @@ -80,6 +80,7 @@ rustfmt rnix-lsp nixpkgs-fmt + notmuch ]; }; } diff --git a/src/config/account.rs b/src/config/account.rs index 2427bee..7ecd4cc 100644 --- a/src/config/account.rs +++ b/src/config/account.rs @@ -89,14 +89,16 @@ impl From> for Accounts { fn from(map: Iter<'_, String, DeserializedAccountConfig>) -> Self { let mut accounts: Vec<_> = map .map(|(name, config)| match config { + #[cfg(feature = "imap-backend")] DeserializedAccountConfig::Imap(config) => { Account::new(name, "imap", config.default.unwrap_or_default()) } + #[cfg(feature = "maildir-backend")] DeserializedAccountConfig::Maildir(config) => { Account::new(name, "maildir", config.default.unwrap_or_default()) } - #[cfg(feature = "notmuch")] - DeserializedAccountConfig::Maildir(config) => { + #[cfg(feature = "notmuch-backend")] + DeserializedAccountConfig::Notmuch(config) => { Account::new(name, "notmuch", config.default.unwrap_or_default()) } }) diff --git a/src/config/account_config.rs b/src/config/account_config.rs index 35ec6c7..ed01a30 100644 --- a/src/config/account_config.rs +++ b/src/config/account_config.rs @@ -69,11 +69,13 @@ impl<'a> AccountConfig { .accounts .iter() .find(|(_, account)| match account { + #[cfg(feature = "imap-backend")] DeserializedAccountConfig::Imap(account) => account.default.unwrap_or_default(), + #[cfg(feature = "maildir-backend")] DeserializedAccountConfig::Maildir(account) => { account.default.unwrap_or_default() } - #[cfg(feature = "notmuch")] + #[cfg(feature = "notmuch-backend")] DeserializedAccountConfig::Notmuch(account) => { account.default.unwrap_or_default() } @@ -169,6 +171,7 @@ impl<'a> AccountConfig { trace!("account config: {:?}", account_config); let backend_config = match account { + #[cfg(feature = "imap-backend")] DeserializedAccountConfig::Imap(config) => BackendConfig::Imap(ImapBackendConfig { imap_host: config.imap_host.clone(), imap_port: config.imap_port.clone(), @@ -177,12 +180,13 @@ impl<'a> AccountConfig { imap_login: config.imap_login.clone(), imap_passwd_cmd: config.imap_passwd_cmd.clone(), }), + #[cfg(feature = "maildir-backend")] DeserializedAccountConfig::Maildir(config) => { BackendConfig::Maildir(MaildirBackendConfig { maildir_dir: shellexpand::full(&config.maildir_dir)?.to_string().into(), }) } - #[cfg(feature = "notmuch")] + #[cfg(feature = "notmuch-backend")] DeserializedAccountConfig::Notmuch(config) => { BackendConfig::Notmuch(NotmuchBackendConfig { notmuch_database_dir: shellexpand::full(&config.notmuch_database_dir)? @@ -315,13 +319,16 @@ impl<'a> AccountConfig { /// Represents all existing kind of account (backend). #[derive(Debug, Clone)] pub enum BackendConfig { + #[cfg(feature = "imap-backend")] Imap(ImapBackendConfig), + #[cfg(feature = "maildir-backend")] Maildir(MaildirBackendConfig), - #[cfg(feature = "notmuch")] + #[cfg(feature = "notmuch-backend")] Notmuch(NotmuchBackendConfig), } /// Represents the IMAP backend. +#[cfg(feature = "imap-backend")] #[derive(Debug, Default, Clone)] pub struct ImapBackendConfig { /// Represents the IMAP host. @@ -338,6 +345,7 @@ pub struct ImapBackendConfig { pub imap_passwd_cmd: String, } +#[cfg(feature = "imap-backend")] impl ImapBackendConfig { /// Gets the IMAP password of the user account. pub fn imap_passwd(&self) -> Result { @@ -350,6 +358,7 @@ impl ImapBackendConfig { } /// Represents the Maildir backend. +#[cfg(feature = "maildir-backend")] #[derive(Debug, Default, Clone)] pub struct MaildirBackendConfig { /// Represents the Maildir directory path. @@ -357,7 +366,7 @@ pub struct MaildirBackendConfig { } /// Represents the Notmuch backend. -#[cfg(feature = "notmuch")] +#[cfg(feature = "notmuch-backend")] #[derive(Debug, Default, Clone)] pub struct NotmuchBackendConfig { /// Represents the Notmuch database path. diff --git a/src/config/account_handlers.rs b/src/config/account_handlers.rs index 6196e84..a4f42cd 100644 --- a/src/config/account_handlers.rs +++ b/src/config/account_handlers.rs @@ -104,8 +104,6 @@ mod tests { } } - struct TestBackend; - let config = DeserializedConfig { accounts: HashMap::from_iter([( "account-1".into(), @@ -119,14 +117,13 @@ mod tests { let account_config = AccountConfig::default(); let mut printer = PrinterServiceTest::default(); - let mut backend = TestBackend {}; assert!(list(None, &config, &account_config, &mut printer).is_ok()); assert_eq!( concat![ "\n", "NAME │BACKEND │DEFAULT \n", - "account-1 │imap │true \n", + "account-1 │imap │yes \n", "\n" ], printer.writter.content diff --git a/src/config/deserialized_account_config.rs b/src/config/deserialized_account_config.rs index 0b6bc92..3164ab1 100644 --- a/src/config/deserialized_account_config.rs +++ b/src/config/deserialized_account_config.rs @@ -11,18 +11,22 @@ pub trait ToDeserializedBaseAccountConfig { #[derive(Debug, Clone, Deserialize)] #[serde(untagged)] pub enum DeserializedAccountConfig { + #[cfg(feature = "imap-backend")] Imap(DeserializedImapAccountConfig), + #[cfg(feature = "maildir-backend")] Maildir(DeserializedMaildirAccountConfig), - #[cfg(feature = "notmuch")] + #[cfg(feature = "notmuch-backend")] Notmuch(DeserializedNotmuchAccountConfig), } impl ToDeserializedBaseAccountConfig for DeserializedAccountConfig { fn to_base(&self) -> DeserializedBaseAccountConfig { match self { + #[cfg(feature = "imap-backend")] Self::Imap(config) => config.to_base(), + #[cfg(feature = "maildir-backend")] Self::Maildir(config) => config.to_base(), - #[cfg(feature = "notmuch")] + #[cfg(feature = "notmuch-backend")] Self::Notmuch(config) => config.to_base(), } } @@ -118,6 +122,7 @@ macro_rules! make_account_config { make_account_config!(DeserializedBaseAccountConfig,); +#[cfg(feature = "imap-backend")] make_account_config!( DeserializedImapAccountConfig, imap_host: String, @@ -128,9 +133,10 @@ make_account_config!( imap_passwd_cmd: String ); +#[cfg(feature = "maildir-backend")] make_account_config!(DeserializedMaildirAccountConfig, maildir_dir: String); -#[cfg(feature = "notmuch")] +#[cfg(feature = "notmuch-backend")] make_account_config!( DeserializedNotmuchAccountConfig, notmuch_database_dir: String diff --git a/src/lib.rs b/src/lib.rs index fb94024..3994874 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,7 @@ pub mod backends { pub mod id_mapper; pub use id_mapper::*; + #[cfg(feature = "imap-backend")] pub mod imap { pub mod imap_args; @@ -62,8 +63,11 @@ pub mod backends { pub mod msg_sort_criterion; } + + #[cfg(feature = "imap-backend")] pub use self::imap::*; + #[cfg(feature = "maildir-backend")] pub mod maildir { pub mod maildir_backend; pub use maildir_backend::*; @@ -77,11 +81,11 @@ pub mod backends { pub mod maildir_flag; pub use maildir_flag::*; } + + #[cfg(feature = "maildir-backend")] pub use self::maildir::*; - #[cfg(feature = "notmuch")] - pub use self::notmuch::*; - #[cfg(feature = "notmuch")] + #[cfg(feature = "notmuch-backend")] pub mod notmuch { pub mod notmuch_backend; pub use notmuch_backend::*; @@ -92,6 +96,9 @@ pub mod backends { pub mod notmuch_envelope; pub use notmuch_envelope::*; } + + #[cfg(feature = "notmuch-backend")] + pub use self::notmuch::*; } pub mod smtp { diff --git a/src/main.rs b/src/main.rs index 12068a7..334005a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use std::{convert::TryFrom, env}; use url::Url; use himalaya::{ - backends::{imap_args, imap_handlers, Backend, ImapBackend, MaildirBackend}, + backends::Backend, compl::{compl_args, compl_handlers}, config::{ account_args, account_handlers, config_args, AccountConfig, BackendConfig, @@ -15,11 +15,17 @@ use himalaya::{ smtp::LettreService, }; -#[cfg(feature = "notmuch")] +#[cfg(feature = "imap-backend")] +use himalaya::backends::{imap_args, imap_handlers, ImapBackend}; + +#[cfg(feature = "maildir-backend")] +use himalaya::backends::MaildirBackend; + +#[cfg(feature = "notmuch-backend")] use himalaya::{backends::NotmuchBackend, config::MaildirBackendConfig}; fn create_app<'a>() -> clap::App<'a, 'a> { - clap::App::new(env!("CARGO_PKG_NAME")) + let app = clap::App::new(env!("CARGO_PKG_NAME")) .version(env!("CARGO_PKG_VERSION")) .about(env!("CARGO_PKG_DESCRIPTION")) .author(env!("CARGO_PKG_AUTHORS")) @@ -29,10 +35,14 @@ fn create_app<'a>() -> clap::App<'a, 'a> { .args(&output_args::args()) .arg(mbox_args::source_arg()) .subcommands(compl_args::subcmds()) - .subcommands(imap_args::subcmds()) .subcommands(account_args::subcmds()) .subcommands(mbox_args::subcmds()) - .subcommands(msg_args::subcmds()) + .subcommands(msg_args::subcmds()); + + #[cfg(feature = "imap-backend")] + let app = app.subcommands(imap_args::subcmds()); + + app } #[allow(clippy::single_match)] @@ -50,22 +60,29 @@ fn main() -> Result<()> { let url = Url::parse(&raw_args[1])?; let mut smtp = LettreService::from(&account_config); + #[cfg(feature = "imap-backend")] let mut imap; + + #[cfg(feature = "maildir-backend")] let mut maildir; - #[cfg(feature = "notmuch")] + + #[cfg(feature = "notmuch-backend")] let maildir_config: MaildirBackendConfig; - #[cfg(feature = "notmuch")] + #[cfg(feature = "notmuch-backend")] let mut notmuch; + let backend: Box<&mut dyn Backend> = match backend_config { + #[cfg(feature = "imap-backend")] BackendConfig::Imap(ref imap_config) => { imap = ImapBackend::new(&account_config, imap_config); Box::new(&mut imap) } + #[cfg(feature = "maildir-backend")] BackendConfig::Maildir(ref maildir_config) => { maildir = MaildirBackend::new(&account_config, maildir_config); Box::new(&mut maildir) } - #[cfg(feature = "notmuch")] + #[cfg(feature = "notmuch-backend")] BackendConfig::Notmuch(ref notmuch_config) => { maildir_config = MaildirBackendConfig { maildir_dir: notmuch_config.notmuch_database_dir.clone(), @@ -100,22 +117,29 @@ fn main() -> Result<()> { .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"))?; + #[cfg(feature = "imap-backend")] let mut imap; + + #[cfg(feature = "maildir-backend")] let mut maildir; - #[cfg(feature = "notmuch")] + + #[cfg(feature = "notmuch-backend")] let maildir_config: MaildirBackendConfig; - #[cfg(feature = "notmuch")] + #[cfg(feature = "notmuch-backend")] let mut notmuch; + let backend: Box<&mut dyn Backend> = match backend_config { + #[cfg(feature = "imap-backend")] BackendConfig::Imap(ref imap_config) => { imap = ImapBackend::new(&account_config, imap_config); Box::new(&mut imap) } + #[cfg(feature = "maildir-backend")] BackendConfig::Maildir(ref maildir_config) => { maildir = MaildirBackend::new(&account_config, maildir_config); Box::new(&mut maildir) } - #[cfg(feature = "notmuch")] + #[cfg(feature = "notmuch-backend")] BackendConfig::Notmuch(ref notmuch_config) => { maildir_config = MaildirBackendConfig { maildir_dir: notmuch_config.notmuch_database_dir.clone(), @@ -129,6 +153,8 @@ fn main() -> Result<()> { let mut smtp = LettreService::from(&account_config); // Check IMAP commands. + #[allow(irrefutable_let_patterns)] + #[cfg(feature = "imap-backend")] if let BackendConfig::Imap(ref imap_config) = backend_config { let mut imap = ImapBackend::new(&account_config, imap_config); match imap_args::matches(&m)? { diff --git a/src/msg/addr_entity.rs b/src/msg/addr_entity.rs index efa7504..f55278d 100644 --- a/src/msg/addr_entity.rs +++ b/src/msg/addr_entity.rs @@ -2,8 +2,7 @@ //! //! This module regroups email address entities and converters. -use anyhow::{Context, Result}; -use log::trace; +use anyhow::Result; use mailparse; use std::fmt::Debug; @@ -63,71 +62,3 @@ pub fn from_addrs_to_sendable_addrs(addrs: &Addrs) -> Result Result { - let name = addr - .name - .as_ref() - .map(|name| { - rfc2047_decoder::decode(&name.to_vec()) - .context("cannot decode address name") - .map(Some) - }) - .unwrap_or(Ok(None))?; - let mbox = addr - .mailbox - .as_ref() - .map(|mbox| { - rfc2047_decoder::decode(&mbox.to_vec()) - .context("cannot decode address mailbox") - .map(Some) - }) - .unwrap_or(Ok(None))?; - let host = addr - .host - .as_ref() - .map(|host| { - rfc2047_decoder::decode(&host.to_vec()) - .context("cannot decode address host") - .map(Some) - }) - .unwrap_or(Ok(None))?; - - trace!("parsing address from imap address"); - trace!("name: {:?}", name); - trace!("mbox: {:?}", mbox); - trace!("host: {:?}", host); - - Ok(Addr::Single(mailparse::SingleInfo { - display_name: name, - addr: match host { - Some(host) => format!("{}@{}", mbox.unwrap_or_default(), host), - None => mbox.unwrap_or_default(), - }, - })) -} - -/// Converts a list of [`imap_proto::Address`] into a list of addresses. -pub fn from_imap_addrs_to_addrs(proto_addrs: &[imap_proto::Address]) -> Result { - let mut addrs = vec![]; - for addr in proto_addrs { - addrs.push( - from_imap_addr_to_addr(addr).context(format!("cannot parse address {:?}", addr))?, - ); - } - Ok(addrs.into()) -} - -/// Converts an optional list of [`imap_proto::Address`] into an optional list of addresses. -pub fn from_imap_addrs_to_some_addrs( - addrs: &Option>, -) -> Result> { - Ok( - if let Some(addrs) = addrs.as_deref().map(from_imap_addrs_to_addrs) { - Some(addrs?) - } else { - None - }, - ) -} diff --git a/tests/test_imap_backend.rs b/tests/test_imap_backend.rs index 7356a92..d0d5035 100644 --- a/tests/test_imap_backend.rs +++ b/tests/test_imap_backend.rs @@ -1,8 +1,10 @@ +#[cfg(feature = "imap-backend")] use himalaya::{ backends::{Backend, ImapBackend, ImapEnvelopes}, config::{AccountConfig, ImapBackendConfig}, }; +#[cfg(feature = "imap-backend")] #[test] fn test_imap_backend() { // configure accounts diff --git a/tests/test_notmuch_backend.rs b/tests/test_notmuch_backend.rs index 183fab9..161fb41 100644 --- a/tests/test_notmuch_backend.rs +++ b/tests/test_notmuch_backend.rs @@ -1,13 +1,13 @@ -#[cfg(feature = "notmuch")] +#[cfg(feature = "notmuch-backend")] use std::{collections::HashMap, env, fs, iter::FromIterator}; -#[cfg(feature = "notmuch")] +#[cfg(feature = "notmuch-backend")] use himalaya::{ backends::{Backend, MaildirBackend, NotmuchBackend, NotmuchEnvelopes}, config::{AccountConfig, MaildirBackendConfig, NotmuchBackendConfig}, }; -#[cfg(feature = "notmuch")] +#[cfg(feature = "notmuch-backend")] #[test] fn test_notmuch_backend() { // set up maildir folders and notmuch database