From c0e002ea1b0b45356333d5c9c402b97e160e8f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Mon, 27 Jun 2022 01:13:55 +0200 Subject: [PATCH] clean process and account modules (#340) --- cli/src/config/account_handlers.rs | 8 +- cli/src/main.rs | 6 +- cli/src/mbox/mbox_handlers.rs | 6 +- cli/src/msg/msg_handlers.rs | 22 +- cli/src/msg/tpl_handlers.rs | 12 +- cli/src/output/print_table.rs | 4 +- cli/src/smtp/smtp_service.rs | 12 +- cli/src/ui/editor.rs | 6 +- cli/src/ui/table.rs | 10 +- lib/src/account/account_config.rs | 192 ++++++++++++------ .../account/deserialized_account_config.rs | 12 +- lib/src/account/deserialized_config.rs | 14 +- lib/src/account/format.rs | 23 --- lib/src/account/hooks.rs | 7 - lib/src/account/mod.rs | 20 +- lib/src/backend/backend.rs | 2 +- lib/src/backend/imap/error.rs | 2 +- lib/src/backend/imap/imap_backend.rs | 10 +- lib/src/backend/maildir/maildir_backend.rs | 9 +- lib/src/backend/notmuch/notmuch_backend.rs | 6 +- lib/src/msg/error.rs | 4 +- lib/src/msg/msg.rs | 26 +-- lib/src/msg/parts.rs | 8 +- lib/src/process.rs | 32 +-- lib/tests/test_imap_backend.rs | 6 +- lib/tests/test_maildir_backend.rs | 6 +- lib/tests/test_notmuch_backend.rs | 2 +- 27 files changed, 251 insertions(+), 216 deletions(-) delete mode 100644 lib/src/account/format.rs delete mode 100644 lib/src/account/hooks.rs diff --git a/cli/src/config/account_handlers.rs b/cli/src/config/account_handlers.rs index 80059d9..4e0e082 100644 --- a/cli/src/config/account_handlers.rs +++ b/cli/src/config/account_handlers.rs @@ -3,7 +3,7 @@ //! This module gathers all account actions triggered by the CLI. use anyhow::Result; -use himalaya_lib::account::{AccountConfig, DeserializedConfig}; +use himalaya_lib::account::{Account, DeserializedConfig}; use log::{info, trace}; use crate::{ @@ -15,7 +15,7 @@ use crate::{ pub fn list<'a, P: PrinterService>( max_width: Option, config: &DeserializedConfig, - account_config: &AccountConfig, + account_config: &Account, printer: &mut P, ) -> Result<()> { info!(">> account list handler"); @@ -38,7 +38,7 @@ pub fn list<'a, P: PrinterService>( #[cfg(test)] mod tests { use himalaya_lib::account::{ - AccountConfig, DeserializedAccountConfig, DeserializedConfig, DeserializedImapAccountConfig, + Account, DeserializedAccountConfig, DeserializedConfig, DeserializedImapAccountConfig, }; use std::{collections::HashMap, fmt::Debug, io, iter::FromIterator}; use termcolor::ColorSpec; @@ -122,7 +122,7 @@ mod tests { ..DeserializedConfig::default() }; - let account_config = AccountConfig::default(); + let account_config = Account::default(); let mut printer = PrinterServiceTest::default(); assert!(list(None, &config, &account_config, &mut printer).is_ok()); diff --git a/cli/src/main.rs b/cli/src/main.rs index b3f834c..e81924f 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,6 +1,6 @@ use anyhow::{Context, Result}; use himalaya_lib::{ - account::{AccountConfig, BackendConfig, DeserializedConfig, DEFAULT_INBOX_FOLDER}, + account::{Account, BackendConfig, DeserializedConfig, DEFAULT_INBOX_FOLDER}, backend::Backend, }; use std::{convert::TryFrom, env}; @@ -58,7 +58,7 @@ fn main() -> Result<()> { if raw_args.len() > 1 && raw_args[1].starts_with("mailto:") { let config = DeserializedConfig::from_opt_path(None)?; let (account_config, backend_config) = - AccountConfig::from_config_and_opt_account_name(&config, None)?; + Account::from_config_and_opt_account_name(&config, None)?; let mut printer = StdoutPrinter::from(OutputFmt::Plain); let url = Url::parse(&raw_args[1])?; let mut smtp = LettreService::from(&account_config); @@ -114,7 +114,7 @@ fn main() -> Result<()> { // Init entities and services. let config = DeserializedConfig::from_opt_path(m.value_of("config"))?; let (account_config, backend_config) = - AccountConfig::from_config_and_opt_account_name(&config, m.value_of("account"))?; + Account::from_config_and_opt_account_name(&config, m.value_of("account"))?; let mbox = m .value_of("mbox-source") .or_else(|| account_config.mailboxes.get("inbox").map(|s| s.as_str())) diff --git a/cli/src/mbox/mbox_handlers.rs b/cli/src/mbox/mbox_handlers.rs index c4d50ac..b5a9aa9 100644 --- a/cli/src/mbox/mbox_handlers.rs +++ b/cli/src/mbox/mbox_handlers.rs @@ -3,7 +3,7 @@ //! This module gathers all mailbox actions triggered by the CLI. use anyhow::Result; -use himalaya_lib::{account::AccountConfig, backend::Backend}; +use himalaya_lib::{account::Account, backend::Backend}; use log::{info, trace}; use crate::output::{PrintTableOpts, PrinterService}; @@ -11,7 +11,7 @@ use crate::output::{PrintTableOpts, PrinterService}; /// Lists all mailboxes. pub fn list<'a, P: PrinterService, B: Backend<'a> + ?Sized>( max_width: Option, - config: &AccountConfig, + config: &Account, printer: &mut P, backend: Box<&'a mut B>, ) -> Result<()> { @@ -170,7 +170,7 @@ mod tests { } } - let config = AccountConfig::default(); + let config = Account::default(); let mut printer = PrinterServiceTest::default(); let mut backend = TestBackend {}; let backend = Box::new(&mut backend); diff --git a/cli/src/msg/msg_handlers.rs b/cli/src/msg/msg_handlers.rs index b18c2c4..4041fdf 100644 --- a/cli/src/msg/msg_handlers.rs +++ b/cli/src/msg/msg_handlers.rs @@ -5,7 +5,7 @@ use anyhow::{Context, Result}; use atty::Stream; use himalaya_lib::{ - account::{AccountConfig, DEFAULT_SENT_FOLDER}, + account::{Account, DEFAULT_SENT_FOLDER}, backend::Backend, msg::{Msg, Part, Parts, TextPlainPart, TplOverride}, }; @@ -28,7 +28,7 @@ use crate::{ pub fn attachments<'a, P: PrinterService, B: Backend<'a> + ?Sized>( seq: &str, mbox: &str, - config: &AccountConfig, + config: &Account, printer: &mut P, backend: Box<&'a mut B>, ) -> Result<()> { @@ -92,7 +92,7 @@ pub fn forward<'a, P: PrinterService, B: Backend<'a> + ?Sized, S: SmtpService>( attachments_paths: Vec<&str>, encrypt: bool, mbox: &str, - config: &AccountConfig, + config: &Account, printer: &mut P, backend: Box<&'a mut B>, smtp: &mut S, @@ -112,7 +112,7 @@ pub fn list<'a, P: PrinterService, B: Backend<'a> + ?Sized>( page_size: Option, page: usize, mbox: &str, - config: &AccountConfig, + config: &Account, printer: &mut P, imap: Box<&'a mut B>, ) -> Result<()> { @@ -134,7 +134,7 @@ pub fn list<'a, P: PrinterService, B: Backend<'a> + ?Sized>( /// [mailto]: https://en.wikipedia.org/wiki/Mailto pub fn mailto<'a, P: PrinterService, B: Backend<'a> + ?Sized, S: SmtpService>( url: &Url, - config: &AccountConfig, + config: &Account, printer: &mut P, backend: Box<&'a mut B>, smtp: &mut S, @@ -212,7 +212,7 @@ pub fn read<'a, P: PrinterService, B: Backend<'a> + ?Sized>( raw: bool, headers: Vec<&str>, mbox: &str, - config: &AccountConfig, + config: &Account, printer: &mut P, backend: Box<&'a mut B>, ) -> Result<()> { @@ -233,7 +233,7 @@ pub fn reply<'a, P: PrinterService, B: Backend<'a> + ?Sized, S: SmtpService>( attachments_paths: Vec<&str>, encrypt: bool, mbox: &str, - config: &AccountConfig, + config: &Account, printer: &mut P, backend: Box<&'a mut B>, smtp: &mut S, @@ -285,7 +285,7 @@ pub fn search<'a, P: PrinterService, B: Backend<'a> + ?Sized>( page_size: Option, page: usize, mbox: &str, - config: &AccountConfig, + config: &Account, printer: &mut P, backend: Box<&'a mut B>, ) -> Result<()> { @@ -310,7 +310,7 @@ pub fn sort<'a, P: PrinterService, B: Backend<'a> + ?Sized>( page_size: Option, page: usize, mbox: &str, - config: &AccountConfig, + config: &Account, printer: &mut P, backend: Box<&'a mut B>, ) -> Result<()> { @@ -330,7 +330,7 @@ pub fn sort<'a, P: PrinterService, B: Backend<'a> + ?Sized>( /// Send a raw message. pub fn send<'a, P: PrinterService, B: Backend<'a> + ?Sized, S: SmtpService>( raw_msg: &str, - config: &AccountConfig, + config: &Account, printer: &mut P, backend: Box<&mut B>, smtp: &mut S, @@ -371,7 +371,7 @@ pub fn write<'a, P: PrinterService, B: Backend<'a> + ?Sized, S: SmtpService>( tpl: TplOverride, attachments_paths: Vec<&str>, encrypt: bool, - config: &AccountConfig, + config: &Account, printer: &mut P, backend: Box<&'a mut B>, smtp: &mut S, diff --git a/cli/src/msg/tpl_handlers.rs b/cli/src/msg/tpl_handlers.rs index 8d8ce14..558e76a 100644 --- a/cli/src/msg/tpl_handlers.rs +++ b/cli/src/msg/tpl_handlers.rs @@ -5,7 +5,7 @@ use anyhow::Result; use atty::Stream; use himalaya_lib::{ - account::AccountConfig, + account::Account, backend::Backend, msg::{Msg, TplOverride}, }; @@ -16,7 +16,7 @@ use crate::{output::PrinterService, smtp::SmtpService}; /// Generate a new message template. pub fn new<'a, P: PrinterService>( opts: TplOverride<'a>, - account: &'a AccountConfig, + account: &'a Account, printer: &'a mut P, ) -> Result<()> { let tpl = Msg::default().to_tpl(opts, account)?; @@ -29,7 +29,7 @@ pub fn reply<'a, P: PrinterService, B: Backend<'a> + ?Sized>( all: bool, opts: TplOverride<'a>, mbox: &str, - config: &'a AccountConfig, + config: &'a Account, printer: &'a mut P, backend: Box<&'a mut B>, ) -> Result<()> { @@ -45,7 +45,7 @@ pub fn forward<'a, P: PrinterService, B: Backend<'a> + ?Sized>( seq: &str, opts: TplOverride<'a>, mbox: &str, - config: &'a AccountConfig, + config: &'a Account, printer: &'a mut P, backend: Box<&'a mut B>, ) -> Result<()> { @@ -59,7 +59,7 @@ pub fn forward<'a, P: PrinterService, B: Backend<'a> + ?Sized>( /// Saves a message based on a template. pub fn save<'a, P: PrinterService, B: Backend<'a> + ?Sized>( mbox: &str, - config: &AccountConfig, + config: &Account, attachments_paths: Vec<&str>, tpl: &str, printer: &mut P, @@ -84,7 +84,7 @@ pub fn save<'a, P: PrinterService, B: Backend<'a> + ?Sized>( /// Sends a message based on a template. pub fn send<'a, P: PrinterService, B: Backend<'a> + ?Sized, S: SmtpService>( mbox: &str, - account: &AccountConfig, + account: &Account, attachments_paths: Vec<&str>, tpl: &str, printer: &mut P, diff --git a/cli/src/output/print_table.rs b/cli/src/output/print_table.rs index 007c2da..a99e316 100644 --- a/cli/src/output/print_table.rs +++ b/cli/src/output/print_table.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use himalaya_lib::account::Format; +use himalaya_lib::account::TextPlainFormat; use std::io; use termcolor::{self, StandardStream}; @@ -12,6 +12,6 @@ pub trait PrintTable { } pub struct PrintTableOpts<'a> { - pub format: &'a Format, + pub format: &'a TextPlainFormat, pub max_width: Option, } diff --git a/cli/src/smtp/smtp_service.rs b/cli/src/smtp/smtp_service.rs index c5c788d..7a9db3b 100644 --- a/cli/src/smtp/smtp_service.rs +++ b/cli/src/smtp/smtp_service.rs @@ -1,5 +1,5 @@ use anyhow::{Context, Result}; -use himalaya_lib::{account::AccountConfig, msg::Msg}; +use himalaya_lib::{account::Account, msg::Msg}; use lettre::{ self, transport::smtp::{ @@ -13,11 +13,11 @@ use std::convert::TryInto; use crate::output::pipe_cmd; pub trait SmtpService { - fn send(&mut self, account: &AccountConfig, msg: &Msg) -> Result>; + fn send(&mut self, account: &Account, msg: &Msg) -> Result>; } pub struct LettreService<'a> { - account: &'a AccountConfig, + account: &'a Account, transport: Option, } @@ -56,7 +56,7 @@ impl LettreService<'_> { } impl SmtpService for LettreService<'_> { - fn send(&mut self, account: &AccountConfig, msg: &Msg) -> Result> { + fn send(&mut self, account: &Account, msg: &Msg) -> Result> { let mut raw_msg = msg.into_sendable_msg(account)?.formatted(); let envelope: lettre::address::Envelope = @@ -76,8 +76,8 @@ impl SmtpService for LettreService<'_> { } } -impl<'a> From<&'a AccountConfig> for LettreService<'a> { - fn from(account: &'a AccountConfig) -> Self { +impl<'a> From<&'a Account> for LettreService<'a> { + fn from(account: &'a Account) -> Self { Self { account, transport: None, diff --git a/cli/src/ui/editor.rs b/cli/src/ui/editor.rs index f940cef..613f6c5 100644 --- a/cli/src/ui/editor.rs +++ b/cli/src/ui/editor.rs @@ -1,6 +1,6 @@ use anyhow::{Context, Result}; use himalaya_lib::{ - account::{AccountConfig, DEFAULT_DRAFT_FOLDER, DEFAULT_SENT_FOLDER}, + account::{Account, DEFAULT_DRAFT_FOLDER, DEFAULT_SENT_FOLDER}, backend::Backend, msg::{local_draft_path, remove_local_draft, Msg, TplOverride}, }; @@ -39,7 +39,7 @@ pub fn open_with_draft() -> Result { open_with_tpl(tpl) } -fn _edit_msg_with_editor(msg: &Msg, tpl: TplOverride, account: &AccountConfig) -> Result { +fn _edit_msg_with_editor(msg: &Msg, tpl: TplOverride, account: &Account) -> Result { let tpl = msg.to_tpl(tpl, account)?; let tpl = open_with_tpl(tpl)?; Msg::from_tpl(&tpl).context("cannot parse message from template") @@ -48,7 +48,7 @@ fn _edit_msg_with_editor(msg: &Msg, tpl: TplOverride, account: &AccountConfig) - pub fn edit_msg_with_editor<'a, P: PrinterService, B: Backend<'a> + ?Sized, S: SmtpService>( mut msg: Msg, tpl: TplOverride, - account: &AccountConfig, + account: &Account, printer: &mut P, backend: Box<&'a mut B>, smtp: &mut S, diff --git a/cli/src/ui/table.rs b/cli/src/ui/table.rs index 2e84209..82fef6f 100644 --- a/cli/src/ui/table.rs +++ b/cli/src/ui/table.rs @@ -5,7 +5,7 @@ //! [builder design pattern]: https://refactoring.guru/design-patterns/builder use anyhow::{Context, Result}; -use himalaya_lib::account::Format; +use himalaya_lib::account::TextPlainFormat; use log::trace; use termcolor::{Color, ColorSpec}; use terminal_size; @@ -169,11 +169,11 @@ where /// Writes the table to the writer. fn print(writer: &mut dyn WriteColor, items: &[Self], opts: PrintTableOpts) -> Result<()> { - let is_format_flowed = matches!(opts.format, Format::Flowed); + let is_format_flowed = matches!(opts.format, TextPlainFormat::Flowed); let max_width = match opts.format { - Format::Fixed(width) => opts.max_width.unwrap_or(*width), - Format::Flowed => 0, - Format::Auto => opts + TextPlainFormat::Fixed(width) => opts.max_width.unwrap_or(*width), + TextPlainFormat::Flowed => 0, + TextPlainFormat::Auto => opts .max_width .or_else(|| terminal_size::terminal_size().map(|(w, _)| w.0 as usize)) .unwrap_or(DEFAULT_TERM_WIDTH), diff --git a/lib/src/account/account_config.rs b/lib/src/account/account_config.rs index 7229bb9..15e3dc9 100644 --- a/lib/src/account/account_config.rs +++ b/lib/src/account/account_config.rs @@ -1,52 +1,78 @@ +//! Account config module. +//! +//! This module contains the representation of the user account. + use lettre::transport::smtp::authentication::Credentials as SmtpCredentials; use log::{debug, info, trace}; use mailparse::MailAddr; +use serde::Deserialize; use shellexpand; -use std::{collections::HashMap, env, ffi::OsStr, fs, path::PathBuf, result}; +use std::{collections::HashMap, env, ffi::OsStr, fs, path::PathBuf}; use thiserror::Error; -use crate::process; +use crate::process::{self, ProcessError}; use super::*; -#[derive(Error, Debug)] -pub enum Error { - #[error("cannot run encrypt file command")] - RunEncryptFileCmdError(#[source] process::Error), - #[error("cannot find pgp encrypt file command from config")] - FindPgpEncryptFileCmdError, - #[error("cannot find pgp decrypt file command from config")] - FindPgpDecryptFileCmdError, +pub const DEFAULT_PAGE_SIZE: usize = 10; +pub const DEFAULT_SIG_DELIM: &str = "-- \n"; + +pub const DEFAULT_INBOX_FOLDER: &str = "INBOX"; +pub const DEFAULT_SENT_FOLDER: &str = "Sent"; +pub const DEFAULT_DRAFT_FOLDER: &str = "Drafts"; + +#[derive(Debug, Error)] +pub enum AccountError { + #[error("cannot encrypt file using pgp")] + EncryptFileError(#[source] ProcessError), + #[error("cannot find encrypt file command from config file")] + EncryptFileMissingCmdError, + + #[error("cannot decrypt file using pgp")] + DecryptFileError(#[source] ProcessError), + #[error("cannot find decrypt file command from config file")] + DecryptFileMissingCmdError, + + #[error("cannot get smtp password")] + GetSmtpPasswdError(#[source] ProcessError), + #[error("cannot get smtp password: password is empty")] + GetSmtpPasswdEmptyError, + + #[cfg(feature = "imap-backend")] + #[error("cannot get imap password")] + GetImapPasswdError(#[source] ProcessError), + #[cfg(feature = "imap-backend")] + #[error("cannot get imap password: password is empty")] + GetImapPasswdEmptyError, #[error("cannot find default account")] FindDefaultAccountError, - #[error("cannot find account \"{0}\"")] + #[error("cannot find account {0}")] FindAccountError(String), - #[error("cannot shell expand")] - ShellExpandError(#[from] shellexpand::LookupError), - #[error("cannot parse account address")] - ParseAccountAddressError(#[from] mailparse::MailParseError), - #[error("cannot find account address from \"{0}\"")] - FindAccountAddressError(String), - #[error("cannot parse download file name from \"{0}\"")] - ParseDownloadFileNameError(PathBuf), - #[error("cannot find password")] - FindPasswordError, - #[error("cannot get smtp password")] - GetSmtpPasswdError(#[source] process::Error), - #[error("cannot get imap password")] - GetImapPasswdError(#[source] process::Error), - #[error("cannot decrypt pgp file")] - DecryptPgpFileError(#[source] process::Error), - #[error("cannot run notify command")] - RunNotifyCmdError(#[source] process::Error), -} + #[error("cannot parse account address {0}")] + ParseAccountAddrError(#[source] mailparse::MailParseError, String), + #[error("cannot find account address in {0}")] + ParseAccountAddrNotFoundError(String), -pub type Result = result::Result; + #[cfg(feature = "maildir-backend")] + #[error("cannot expand maildir path")] + ExpandMaildirPathError(#[source] shellexpand::LookupError), + #[cfg(feature = "notmuch-backend")] + #[error("cannot expand notmuch path")] + ExpandNotmuchDatabasePathError(#[source] shellexpand::LookupError), + #[error("cannot expand mailbox alias {1}")] + ExpandMboxAliasError(#[source] shellexpand::LookupError, String), + + #[error("cannot parse download file name from {0}")] + ParseDownloadFileNameError(PathBuf), + + #[error("cannot start the notify mode")] + StartNotifyModeError(#[source] ProcessError), +} /// Represents the user account. #[derive(Debug, Default, Clone)] -pub struct AccountConfig { +pub struct Account { /// Represents the name of the user account. pub name: String, /// Makes this account the default one. @@ -69,7 +95,7 @@ pub struct AccountConfig { pub watch_cmds: Vec, /// Represents the text/plain format as defined in the /// [RFC2646](https://www.ietf.org/rfc/rfc2646.txt) - pub format: Format, + pub format: TextPlainFormat, /// Overrides the default headers displayed at the top of /// the read message. pub read_headers: Vec, @@ -99,13 +125,13 @@ pub struct AccountConfig { pub pgp_decrypt_cmd: Option, } -impl<'a> AccountConfig { +impl<'a> Account { /// Tries to create an account from a config and an optional /// account name. pub fn from_config_and_opt_account_name( config: &'a DeserializedConfig, account_name: Option<&str>, - ) -> Result<(AccountConfig, BackendConfig)> { + ) -> Result<(Account, BackendConfig), AccountError> { info!("begin: parsing account and backend configs from config and account name"); debug!("account name: {:?}", account_name.unwrap_or("default")); @@ -126,12 +152,12 @@ impl<'a> AccountConfig { } }) .map(|(name, account)| (name.to_owned(), account)) - .ok_or_else(|| Error::FindDefaultAccountError), + .ok_or_else(|| AccountError::FindDefaultAccountError), Some(name) => config .accounts .get(name) .map(|account| (name.to_owned(), account)) - .ok_or_else(|| Error::FindAccountError(name.to_owned())), + .ok_or_else(|| AccountError::FindAccountError(name.to_owned())), }?; let base_account = account.to_base(); @@ -175,7 +201,7 @@ impl<'a> AccountConfig { .or_else(|| sig.map(|sig| sig.to_owned())) .map(|sig| format!("{}{}", sig_delim, sig.trim_end())); - let account_config = AccountConfig { + let account_config = Account { name, display_name: base_account .name @@ -234,13 +260,17 @@ impl<'a> AccountConfig { #[cfg(feature = "maildir-backend")] DeserializedAccountConfig::Maildir(config) => { BackendConfig::Maildir(MaildirBackendConfig { - maildir_dir: shellexpand::full(&config.maildir_dir)?.to_string().into(), + maildir_dir: shellexpand::full(&config.maildir_dir) + .map_err(AccountError::ExpandMaildirPathError)? + .to_string() + .into(), }) } #[cfg(feature = "notmuch-backend")] DeserializedAccountConfig::Notmuch(config) => { BackendConfig::Notmuch(NotmuchBackendConfig { - notmuch_database_dir: shellexpand::full(&config.notmuch_database_dir)? + notmuch_database_dir: shellexpand::full(&config.notmuch_database_dir) + .map_err(AccountError::ExpandNotmuchDatabasePathError)? .to_string() .into(), }) @@ -253,7 +283,7 @@ impl<'a> AccountConfig { } /// Builds the full RFC822 compliant address of the user account. - pub fn address(&self) -> Result { + pub fn address(&self) -> Result { let has_special_chars = "()<>[]:;@.,".contains(|c| self.display_name.contains(c)); let addr = if self.display_name.is_empty() { self.email.clone() @@ -264,19 +294,21 @@ impl<'a> AccountConfig { format!("{} <{}>", self.display_name, self.email) }; - Ok(mailparse::addrparse(&addr)? + Ok(mailparse::addrparse(&addr) + .map_err(|err| AccountError::ParseAccountAddrError(err, addr.to_owned()))? .first() - .ok_or_else(|| Error::FindAccountAddressError(addr.into()))? + .ok_or_else(|| AccountError::ParseAccountAddrNotFoundError(addr.to_owned()))? .clone()) } /// Builds the user account SMTP credentials. - pub fn smtp_creds(&self) -> Result { - let passwd = process::run_cmd(&self.smtp_passwd_cmd).map_err(Error::GetSmtpPasswdError)?; + pub fn smtp_creds(&self) -> Result { + let passwd = + process::run(&self.smtp_passwd_cmd).map_err(AccountError::GetSmtpPasswdError)?; let passwd = passwd .lines() .next() - .ok_or_else(|| Error::FindPasswordError)?; + .ok_or_else(|| AccountError::GetSmtpPasswdEmptyError)?; Ok(SmtpCredentials::new( self.smtp_login.to_owned(), @@ -285,27 +317,30 @@ impl<'a> AccountConfig { } /// Encrypts a file. - pub fn pgp_encrypt_file(&self, addr: &str, path: PathBuf) -> Result { + pub fn pgp_encrypt_file(&self, addr: &str, path: PathBuf) -> Result { if let Some(cmd) = self.pgp_encrypt_cmd.as_ref() { let encrypt_file_cmd = format!("{} {} {:?}", cmd, addr, path); - Ok(process::run_cmd(&encrypt_file_cmd).map_err(Error::RunEncryptFileCmdError)?) + Ok(process::run(&encrypt_file_cmd).map_err(AccountError::EncryptFileError)?) } else { - Err(Error::FindPgpEncryptFileCmdError) + Err(AccountError::EncryptFileMissingCmdError) } } /// Decrypts a file. - pub fn pgp_decrypt_file(&self, path: PathBuf) -> Result { + pub fn pgp_decrypt_file(&self, path: PathBuf) -> Result { if let Some(cmd) = self.pgp_decrypt_cmd.as_ref() { let decrypt_file_cmd = format!("{} {:?}", cmd, path); - Ok(process::run_cmd(&decrypt_file_cmd).map_err(Error::DecryptPgpFileError)?) + Ok(process::run(&decrypt_file_cmd).map_err(AccountError::DecryptFileError)?) } else { - Err(Error::FindPgpDecryptFileCmdError) + Err(AccountError::DecryptFileMissingCmdError) } } /// Gets the download path from a file name. - pub fn get_download_file_path>(&self, file_name: S) -> Result { + pub fn get_download_file_path>( + &self, + file_name: S, + ) -> Result { let file_path = self.downloads_dir.join(file_name.as_ref()); self.get_unique_download_file_path(&file_path, |path, _count| path.is_file()) } @@ -316,7 +351,7 @@ impl<'a> AccountConfig { &self, original_file_path: &PathBuf, is_file: impl Fn(&PathBuf, u8) -> bool, - ) -> Result { + ) -> Result { let mut count = 0; let file_ext = original_file_path .extension() @@ -332,7 +367,9 @@ impl<'a> AccountConfig { .file_stem() .and_then(OsStr::to_str) .map(|fstem| format!("{}_{}{}", fstem, count, file_ext)) - .ok_or_else(|| Error::ParseDownloadFileNameError(file_path.to_owned()))?, + .ok_or_else(|| { + AccountError::ParseDownloadFileNameError(file_path.to_owned()) + })?, )); } @@ -340,7 +377,7 @@ impl<'a> AccountConfig { } /// Runs the notify command. - pub fn run_notify_cmd>(&self, subject: S, sender: S) -> Result<()> { + pub fn run_notify_cmd>(&self, subject: S, sender: S) -> Result<(), AccountError> { let subject = subject.as_ref(); let sender = sender.as_ref(); @@ -351,19 +388,21 @@ impl<'a> AccountConfig { .map(|cmd| format!(r#"{} {:?} {:?}"#, cmd, subject, sender)) .unwrap_or(default_cmd); - process::run_cmd(&cmd).map_err(Error::RunNotifyCmdError)?; + process::run(&cmd).map_err(AccountError::StartNotifyModeError)?; Ok(()) } /// Gets the mailbox alias if exists, otherwise returns the /// mailbox. Also tries to expand shell variables. - pub fn get_mbox_alias(&self, mbox: &str) -> Result { + pub fn get_mbox_alias(&self, mbox: &str) -> Result { let mbox = self .mailboxes .get(&mbox.trim().to_lowercase()) .map(|s| s.as_str()) .unwrap_or(mbox); - let mbox = shellexpand::full(mbox).map(String::from)?; + let mbox = shellexpand::full(mbox) + .map(String::from) + .map_err(|err| AccountError::ExpandMboxAliasError(err, mbox.to_owned()))?; Ok(mbox) } } @@ -400,12 +439,13 @@ pub struct ImapBackendConfig { #[cfg(feature = "imap-backend")] impl ImapBackendConfig { /// Gets the IMAP password of the user account. - pub fn imap_passwd(&self) -> Result { - let passwd = process::run_cmd(&self.imap_passwd_cmd).map_err(Error::GetImapPasswdError)?; + pub fn imap_passwd(&self) -> Result { + let passwd = + process::run(&self.imap_passwd_cmd).map_err(AccountError::GetImapPasswdError)?; let passwd = passwd .lines() .next() - .ok_or_else(|| Error::FindPasswordError)?; + .ok_or_else(|| AccountError::GetImapPasswdEmptyError)?; Ok(passwd.to_string()) } } @@ -426,13 +466,39 @@ pub struct NotmuchBackendConfig { pub notmuch_database_dir: PathBuf, } +/// Represents the text/plain format as defined in the [RFC2646]. +/// +/// [RFC2646]: https://www.ietf.org/rfc/rfc2646.txt +#[derive(Debug, Clone, Eq, PartialEq, Deserialize)] +#[serde(tag = "type", content = "width", rename_all = "lowercase")] +pub enum TextPlainFormat { + // Forces the content width with a fixed amount of pixels. + Fixed(usize), + // Makes the content fit the terminal. + Auto, + // Does not restrict the content. + Flowed, +} + +impl Default for TextPlainFormat { + fn default() -> Self { + Self::Auto + } +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct Hooks { + pub pre_send: Option, +} + #[cfg(test)] mod tests { use super::*; #[test] fn it_should_get_unique_download_file_path() { - let account = AccountConfig::default(); + let account = Account::default(); let path = PathBuf::from("downloads/file.ext"); // When file path is unique diff --git a/lib/src/account/deserialized_account_config.rs b/lib/src/account/deserialized_account_config.rs index 7539a41..8ba6278 100644 --- a/lib/src/account/deserialized_account_config.rs +++ b/lib/src/account/deserialized_account_config.rs @@ -1,7 +1,12 @@ +//! Deserialized account config module. +//! +//! This module contains the raw deserialized representation of an +//! account in the accounts section of the user configuration file. + use serde::Deserialize; use std::{collections::HashMap, path::PathBuf}; -use crate::account::{Format, Hooks}; +use super::*; pub trait ToDeserializedBaseAccountConfig { fn to_base(&self) -> DeserializedBaseAccountConfig; @@ -53,9 +58,8 @@ macro_rules! make_account_config { pub notify_query: Option, /// Overrides the watch commands for this account. pub watch_cmds: Option>, - /// Represents the text/plain format as defined in the - /// [RFC2646](https://www.ietf.org/rfc/rfc2646.txt) - pub format: Option, + /// Represents the text/plain format. + pub format: Option, /// Represents the default headers displayed at the top of /// the read message. #[serde(default)] diff --git a/lib/src/account/deserialized_config.rs b/lib/src/account/deserialized_config.rs index 500d00a..cc4280e 100644 --- a/lib/src/account/deserialized_config.rs +++ b/lib/src/account/deserialized_config.rs @@ -1,17 +1,15 @@ +//! Deserialized config module. +//! +//! This module contains the raw deserialized representation of the +//! user configuration file. + use log::{debug, trace}; use serde::Deserialize; use std::{collections::HashMap, env, fs, io, path::PathBuf, result}; use thiserror::Error; use toml; -use crate::account::DeserializedAccountConfig; - -pub const DEFAULT_PAGE_SIZE: usize = 10; -pub const DEFAULT_SIG_DELIM: &str = "-- \n"; - -pub const DEFAULT_INBOX_FOLDER: &str = "INBOX"; -pub const DEFAULT_SENT_FOLDER: &str = "Sent"; -pub const DEFAULT_DRAFT_FOLDER: &str = "Drafts"; +use super::*; #[derive(Error, Debug)] pub enum DeserializeConfigError { diff --git a/lib/src/account/format.rs b/lib/src/account/format.rs deleted file mode 100644 index 304b7f6..0000000 --- a/lib/src/account/format.rs +++ /dev/null @@ -1,23 +0,0 @@ -use serde::Deserialize; - -/// Represents the text/plain format as defined in the [RFC2646]. The -/// format is then used by the table system to adjust the way it is -/// rendered. -/// -/// [RFC2646]: https://www.ietf.org/rfc/rfc2646.txt -#[derive(Debug, Clone, Eq, PartialEq, Deserialize)] -#[serde(tag = "type", content = "width", rename_all = "lowercase")] -pub enum Format { - // Forces the content width with a fixed amount of pixels. - Fixed(usize), - // Makes the content fit the terminal. - Auto, - // Does not restrict the content. - Flowed, -} - -impl Default for Format { - fn default() -> Self { - Self::Auto - } -} diff --git a/lib/src/account/hooks.rs b/lib/src/account/hooks.rs deleted file mode 100644 index 4bd44f0..0000000 --- a/lib/src/account/hooks.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::Deserialize; - -#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub struct Hooks { - pub pre_send: Option, -} diff --git a/lib/src/account/mod.rs b/lib/src/account/mod.rs index 5409db8..01a8c4d 100644 --- a/lib/src/account/mod.rs +++ b/lib/src/account/mod.rs @@ -1,16 +1,12 @@ -pub mod deserialized_config; -pub use deserialized_config::*; +//! Account module. +//! +//! This module contains everything related to the user configuration. -pub mod deserialized_account_config; -pub use deserialized_account_config::*; - -// pub mod account_handlers; - -pub mod account_config; +mod account_config; pub use account_config::*; -pub mod format; -pub use format::*; +mod deserialized_config; +pub use deserialized_config::*; -pub mod hooks; -pub use hooks::*; +mod deserialized_account_config; +pub use deserialized_account_config::*; diff --git a/lib/src/backend/backend.rs b/lib/src/backend/backend.rs index 2030347..b1d1d99 100644 --- a/lib/src/backend/backend.rs +++ b/lib/src/backend/backend.rs @@ -27,7 +27,7 @@ pub enum Error { ImapError(#[from] super::imap::Error), #[error(transparent)] - AccountError(#[from] account::Error), + AccountError(#[from] account::AccountError), #[error(transparent)] MsgError(#[from] msg::Error), diff --git a/lib/src/backend/imap/error.rs b/lib/src/backend/imap/error.rs index 76454a3..ff3b233 100644 --- a/lib/src/backend/imap/error.rs +++ b/lib/src/backend/imap/error.rs @@ -78,7 +78,7 @@ pub enum Error { LogoutError(#[source] imap::Error), #[error(transparent)] - AccountError(#[from] account::Error), + AccountError(#[from] account::AccountError), #[error(transparent)] MsgError(#[from] msg::Error), } diff --git a/lib/src/backend/imap/imap_backend.rs b/lib/src/backend/imap/imap_backend.rs index d5bcee5..eb8f2a0 100644 --- a/lib/src/backend/imap/imap_backend.rs +++ b/lib/src/backend/imap/imap_backend.rs @@ -8,26 +8,26 @@ use native_tls::{TlsConnector, TlsStream}; use std::{collections::HashSet, convert::TryInto, net::TcpStream, thread}; use crate::{ - account::{AccountConfig, ImapBackendConfig}, + account::{Account, ImapBackendConfig}, backend::{ backend::Result, from_imap_fetch, from_imap_fetches, imap::msg_sort_criterion::SortCriteria, imap::Error, into_imap_flags, Backend, }, mbox::{Mbox, Mboxes}, msg::{Envelopes, Flags, Msg}, - process::run_cmd, + process, }; type ImapSess = imap::Session>; pub struct ImapBackend<'a> { - account_config: &'a AccountConfig, + account_config: &'a Account, imap_config: &'a ImapBackendConfig, sess: Option, } impl<'a> ImapBackend<'a> { - pub fn new(account_config: &'a AccountConfig, imap_config: &'a ImapBackendConfig) -> Self { + pub fn new(account_config: &'a Account, imap_config: &'a ImapBackendConfig) -> Self { Self { account_config, imap_config, @@ -187,7 +187,7 @@ impl<'a> ImapBackend<'a> { debug!("batch execution of {} cmd(s)", cmds.len()); cmds.iter().for_each(|cmd| { debug!("running command {:?}…", cmd); - let res = run_cmd(cmd); + let res = process::run(cmd); debug!("{:?}", res); }) }); diff --git a/lib/src/backend/maildir/maildir_backend.rs b/lib/src/backend/maildir/maildir_backend.rs index c031246..2c50328 100644 --- a/lib/src/backend/maildir/maildir_backend.rs +++ b/lib/src/backend/maildir/maildir_backend.rs @@ -7,7 +7,7 @@ use log::{debug, info, trace}; use std::{env, ffi::OsStr, fs, path::PathBuf}; use crate::{ - account::{AccountConfig, MaildirBackendConfig}, + account::{Account, MaildirBackendConfig}, backend::{backend::Result, maildir_envelopes, maildir_flags, Backend, IdMapper}, mbox::{Mbox, Mboxes}, msg::{Envelopes, Flags, Msg}, @@ -17,15 +17,12 @@ use super::MaildirError; /// Represents the maildir backend. pub struct MaildirBackend<'a> { - account_config: &'a AccountConfig, + account_config: &'a Account, mdir: maildir::Maildir, } impl<'a> MaildirBackend<'a> { - pub fn new( - account_config: &'a AccountConfig, - maildir_config: &'a MaildirBackendConfig, - ) -> Self { + pub fn new(account_config: &'a Account, maildir_config: &'a MaildirBackendConfig) -> Self { Self { account_config, mdir: maildir_config.maildir_dir.clone().into(), diff --git a/lib/src/backend/notmuch/notmuch_backend.rs b/lib/src/backend/notmuch/notmuch_backend.rs index 8918c9b..e249d46 100644 --- a/lib/src/backend/notmuch/notmuch_backend.rs +++ b/lib/src/backend/notmuch/notmuch_backend.rs @@ -2,7 +2,7 @@ use log::{debug, info, trace}; use std::fs; use crate::{ - account::{AccountConfig, NotmuchBackendConfig}, + account::{Account, NotmuchBackendConfig}, backend::{ backend::Result, notmuch_envelopes, Backend, IdMapper, MaildirBackend, NotmuchError, }, @@ -12,7 +12,7 @@ use crate::{ /// Represents the Notmuch backend. pub struct NotmuchBackend<'a> { - account_config: &'a AccountConfig, + account_config: &'a Account, notmuch_config: &'a NotmuchBackendConfig, pub mdir: &'a mut MaildirBackend<'a>, db: notmuch::Database, @@ -20,7 +20,7 @@ pub struct NotmuchBackend<'a> { impl<'a> NotmuchBackend<'a> { pub fn new( - account_config: &'a AccountConfig, + account_config: &'a Account, notmuch_config: &'a NotmuchBackendConfig, mdir: &'a mut MaildirBackend<'a>, ) -> Result> { diff --git a/lib/src/msg/error.rs b/lib/src/msg/error.rs index 0cf689f..33d4473 100644 --- a/lib/src/msg/error.rs +++ b/lib/src/msg/error.rs @@ -34,7 +34,7 @@ pub enum Error { ParseAddressError(#[from] lettre::address::AddressError), #[error(transparent)] - AccountError(#[from] account::Error), + AccountError(#[from] account::AccountError), #[error("cannot get content type of multipart")] GetMultipartContentTypeError, @@ -47,7 +47,7 @@ pub enum Error { #[error("cannot write encrypted part to temporary file")] WriteEncryptedPartBodyError(#[source] io::Error), #[error("cannot write encrypted part to temporary file")] - DecryptPartError(#[source] account::Error), + DecryptPartError(#[source] account::AccountError), #[error("cannot delete local draft: {1}")] DeleteLocalDraftError(#[source] io::Error, path::PathBuf), diff --git a/lib/src/msg/msg.rs b/lib/src/msg/msg.rs index 64f04f8..264624c 100644 --- a/lib/src/msg/msg.rs +++ b/lib/src/msg/msg.rs @@ -17,7 +17,7 @@ use tree_magic; use uuid::Uuid; use crate::{ - account::{AccountConfig, DEFAULT_SIG_DELIM}, + account::{Account, DEFAULT_SIG_DELIM}, msg::{ from_addrs_to_sendable_addrs, from_addrs_to_sendable_mbox, from_slice_to_addrs, Addr, Addrs, BinaryPart, Error, Part, Parts, Result, TextPlainPart, TplOverride, @@ -166,7 +166,7 @@ impl Msg { } } - pub fn into_reply(mut self, all: bool, account: &AccountConfig) -> Result { + pub fn into_reply(mut self, all: bool, account: &Account) -> Result { let account_addr = account.address()?; // In-Reply-To @@ -264,7 +264,7 @@ impl Msg { Ok(self) } - pub fn into_forward(mut self, account: &AccountConfig) -> Result { + pub fn into_forward(mut self, account: &Account) -> Result { let account_addr = account.address()?; let prev_subject = self.subject.to_owned(); @@ -380,7 +380,7 @@ impl Msg { } } - pub fn to_tpl(&self, opts: TplOverride, account: &AccountConfig) -> Result { + pub fn to_tpl(&self, opts: TplOverride, account: &Account) -> Result { let account_addr: Addrs = vec![account.address()?].into(); let mut tpl = String::default(); @@ -463,10 +463,10 @@ impl Msg { let parsed_mail = mailparse::parse_mail(tpl.as_bytes()).map_err(Error::ParseTplError)?; info!("end: building message from template"); - Self::from_parsed_mail(parsed_mail, &AccountConfig::default()) + Self::from_parsed_mail(parsed_mail, &Account::default()) } - pub fn into_sendable_msg(&self, account: &AccountConfig) -> Result { + pub fn into_sendable_msg(&self, account: &Account) -> Result { let mut msg_builder = lettre::Message::builder() .message_id(self.message_id.to_owned()) .subject(self.subject.to_owned()); @@ -551,7 +551,7 @@ impl Msg { pub fn from_parsed_mail( parsed_mail: mailparse::ParsedMail<'_>, - config: &AccountConfig, + config: &Account, ) -> Result { trace!(">> build message from parsed mail"); trace!("parsed mail: {:?}", parsed_mail); @@ -623,7 +623,7 @@ impl Msg { &self, text_mime: &str, headers: Vec<&str>, - config: &AccountConfig, + config: &Account, ) -> Result { let mut all_headers = vec![]; for h in config.read_headers.iter() { @@ -750,10 +750,10 @@ mod tests { #[test] fn test_into_reply() { - let config = AccountConfig { + let config = Account { display_name: "Test".into(), email: "test-account@local".into(), - ..AccountConfig::default() + ..Account::default() }; // Checks that: @@ -889,7 +889,7 @@ mod tests { #[test] fn test_to_readable() { - let config = AccountConfig::default(); + let config = Account::default(); let msg = Msg { parts: Parts(vec![Part::TextPlain(TextPlainPart { content: String::from("hello, world!"), @@ -952,14 +952,14 @@ mod tests { .unwrap() ); - let config = AccountConfig { + let config = Account { read_headers: vec![ "CusTOM-heaDER".into(), "Subject".into(), "from".into(), "cc".into(), ], - ..AccountConfig::default() + ..Account::default() }; // header present but empty in msg headers, empty config assert_eq!( diff --git a/lib/src/msg/parts.rs b/lib/src/msg/parts.rs index 9de2bdb..b64c01a 100644 --- a/lib/src/msg/parts.rs +++ b/lib/src/msg/parts.rs @@ -6,7 +6,7 @@ use std::{ }; use uuid::Uuid; -use crate::{account::AccountConfig, msg}; +use crate::{account::Account, msg}; #[derive(Debug, Clone, Default, Serialize)] pub struct TextPlainPart { @@ -50,7 +50,7 @@ impl Parts { } pub fn from_parsed_mail<'a>( - account: &'a AccountConfig, + account: &'a Account, part: &'a mailparse::ParsedMail<'a>, ) -> msg::Result { let mut parts = vec![]; @@ -80,7 +80,7 @@ impl DerefMut for Parts { } fn build_parts_map_rec( - account: &AccountConfig, + account: &Account, parsed_mail: &mailparse::ParsedMail, parts: &mut Vec, ) -> msg::Result<()> { @@ -137,7 +137,7 @@ fn build_parts_map_rec( Ok(()) } -fn decrypt_part(account: &AccountConfig, msg: &mailparse::ParsedMail) -> msg::Result { +fn decrypt_part(account: &Account, msg: &mailparse::ParsedMail) -> msg::Result { let msg_path = env::temp_dir().join(Uuid::new_v4().to_string()); let msg_body = msg .get_body() diff --git a/lib/src/process.rs b/lib/src/process.rs index 36bc526..c9e282c 100644 --- a/lib/src/process.rs +++ b/lib/src/process.rs @@ -1,30 +1,34 @@ +//! Process module. +//! +//! This module contains cross platform helpers around the +//! `std::process` crate. + use log::{debug, trace}; -use std::{io, process, result, string}; +use std::{io, process::Command, string}; use thiserror::Error; -#[derive(Error, Debug)] -pub enum Error { - #[error("cannot run command: {1}")] +#[derive(Debug, Error)] +pub enum ProcessError { + #[error("cannot run command {1:?}")] RunCmdError(#[source] io::Error, String), + #[error("cannot parse command output")] ParseCmdOutputError(#[source] string::FromUtf8Error), } -pub type Result = result::Result; - -pub fn run_cmd(cmd: &str) -> Result { - trace!(">> run command"); +pub fn run(cmd: &str) -> Result { + debug!(">> run command"); debug!("command: {}", cmd); let output = if cfg!(target_os = "windows") { - process::Command::new("cmd").args(&["/C", cmd]).output() + Command::new("cmd").args(&["/C", cmd]).output() } else { - process::Command::new("sh").arg("-c").arg(cmd).output() + Command::new("sh").arg("-c").arg(cmd).output() }; - let output = output.map_err(|err| Error::RunCmdError(err, cmd.to_string()))?; - let output = String::from_utf8(output.stdout).map_err(Error::ParseCmdOutputError)?; + let output = output.map_err(|err| ProcessError::RunCmdError(err, cmd.to_string()))?; + let output = String::from_utf8(output.stdout).map_err(ProcessError::ParseCmdOutputError)?; - debug!("command output: {}", output); - trace!("<< run command"); + trace!("command output: {}", output); + debug!("<< run command"); Ok(output) } diff --git a/lib/tests/test_imap_backend.rs b/lib/tests/test_imap_backend.rs index 3235822..081d045 100644 --- a/lib/tests/test_imap_backend.rs +++ b/lib/tests/test_imap_backend.rs @@ -1,6 +1,6 @@ #[cfg(feature = "imap-backend")] use himalaya_lib::{ - account::{AccountConfig, ImapBackendConfig}, + account::{Account, ImapBackendConfig}, backend::{Backend, ImapBackend}, }; @@ -8,14 +8,14 @@ use himalaya_lib::{ #[test] fn test_imap_backend() { // configure accounts - let account_config = AccountConfig { + let account_config = Account { smtp_host: "localhost".into(), smtp_port: 3465, smtp_starttls: false, smtp_insecure: true, smtp_login: "inbox@localhost".into(), smtp_passwd_cmd: "echo 'password'".into(), - ..AccountConfig::default() + ..Account::default() }; let imap_config = ImapBackendConfig { imap_host: "localhost".into(), diff --git a/lib/tests/test_maildir_backend.rs b/lib/tests/test_maildir_backend.rs index 9a9e22a..8077aaf 100644 --- a/lib/tests/test_maildir_backend.rs +++ b/lib/tests/test_maildir_backend.rs @@ -2,7 +2,7 @@ use maildir::Maildir; use std::{collections::HashMap, env, fs, iter::FromIterator}; use himalaya_lib::{ - account::{AccountConfig, MaildirBackendConfig}, + account::{Account, MaildirBackendConfig}, backend::{Backend, MaildirBackend}, msg::Flag, }; @@ -19,9 +19,9 @@ fn test_maildir_backend() { mdir_sub.create_dirs().unwrap(); // configure accounts - let account_config = AccountConfig { + let account_config = Account { mailboxes: HashMap::from_iter([("subdir".into(), "Subdir".into())]), - ..AccountConfig::default() + ..Account::default() }; let mdir_config = MaildirBackendConfig { maildir_dir: mdir.path().to_owned(), diff --git a/lib/tests/test_notmuch_backend.rs b/lib/tests/test_notmuch_backend.rs index 1a886e7..dae4e43 100644 --- a/lib/tests/test_notmuch_backend.rs +++ b/lib/tests/test_notmuch_backend.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, env, fs, iter::FromIterator}; #[cfg(feature = "notmuch-backend")] use himalaya_lib::{ - account::{AccountConfig, MaildirBackendConfig, NotmuchBackendConfig}, + account::{Account, MaildirBackendConfig, NotmuchBackendConfig}, backend::{Backend, MaildirBackend, NotmuchBackend}, };