diff --git a/src/components/mail.rs b/src/components/mail.rs index 141bf2e1..f0961b23 100644 --- a/src/components/mail.rs +++ b/src/components/mail.rs @@ -39,7 +39,7 @@ mod status; pub use self::status::*; fn get_display_name(context: &Context, idx: usize) -> String { - let settings = context.accounts[idx].runtime_settings.account(); + let settings = context.accounts[idx].settings.account(); if let Some(d) = settings.display_name.as_ref() { format!("{} <{}>", d, settings.identity) } else { diff --git a/src/components/mail/compose.rs b/src/components/mail/compose.rs index 204cf0ad..9ce5a462 100644 --- a/src/components/mail/compose.rs +++ b/src/components/mail/compose.rs @@ -147,7 +147,9 @@ impl Composer { id: ComponentId::new_v4(), ..Default::default() }; - for (h, v) in context.settings.composing.default_header_values.iter() { + for (h, v) in + mailbox_acc_settings!(context[account_cursor].composing.default_header_values).iter() + { if v.is_empty() { continue; } @@ -298,12 +300,9 @@ impl Composer { write_string_to_grid( &format!( "☑ sign with {}", - context - .settings - .pgp - .key + mailbox_acc_settings!(context[self.account_cursor].pgp.key) .as_ref() - .map(String::as_str) + .map(|s| s.as_str()) .unwrap_or("default key") ), grid, @@ -392,7 +391,9 @@ impl Component for Composer { if !self.initialized { if self.sign_mail.is_unset() { - self.sign_mail = ToggleFlag::InternalVal(context.settings.pgp.auto_sign); + self.sign_mail = ToggleFlag::InternalVal(*mailbox_acc_settings!( + context[self.account_cursor].pgp.auto_sign + )); } if !self.draft.headers().contains_key("From") || self.draft.headers()["From"].is_empty() { @@ -859,8 +860,10 @@ impl Component for Composer { && shortcut!(key == shortcuts[Self::DESCRIPTION]["edit_mail"]) => { /* Edit draft in $EDITOR */ - let settings = &context.settings; - let editor = if let Some(editor_cmd) = settings.composing.editor_cmd.as_ref() { + let editor = if let Some(editor_cmd) = + mailbox_acc_settings!(context[self.account_cursor].composing.editor_cmd) + .as_ref() + { editor_cmd.to_string() } else { match std::env::var("EDITOR") { @@ -884,7 +887,7 @@ impl Component for Composer { true, ); - if settings.composing.embed { + if *mailbox_acc_settings!(context[self.account_cursor].composing.embed) { self.embed = Some(EmbedStatus::Running( crate::terminal::embed::create_pty( width!(self.embed_area), @@ -1124,7 +1127,8 @@ impl Component for Composer { Default::default() }; - let our_map: ShortcutMap = context.settings.shortcuts.composing.key_values(); + let our_map: ShortcutMap = + mailbox_acc_settings!(context[self.account_cursor].shortcuts.composing).key_values(); map.insert(Composer::DESCRIPTION, our_map); map @@ -1178,9 +1182,10 @@ pub fn send_draft( ) -> bool { use std::io::Write; use std::process::{Command, Stdio}; - let settings = &context.settings; - let format_flowed = settings.composing.format_flowed; - let parts = split_command!(settings.composing.mailer_cmd); + let format_flowed = *mailbox_acc_settings!(context[account_cursor].composing.format_flowed); + let parts = split_command!(mailbox_acc_settings!( + context[account_cursor].composing.mailer_cmd + )); if parts.is_empty() { context.replies.push_back(UIEvent::Notification( None, @@ -1233,8 +1238,12 @@ pub fn send_draft( } let output = crate::components::mail::pgp::sign( body.into(), - context.settings.pgp.gpg_binary.as_ref().map(String::as_str), - context.settings.pgp.key.as_ref().map(String::as_str), + mailbox_acc_settings!(context[account_cursor].pgp.gpg_binary) + .as_ref() + .map(|s| s.as_str()), + mailbox_acc_settings!(context[account_cursor].pgp.key) + .as_ref() + .map(|s| s.as_str()), ); if let Err(e) = &output { debug!("{:?} could not sign draft msg", e); diff --git a/src/components/mail/listing.rs b/src/components/mail/listing.rs index 07ba1bc2..e7f0e1cc 100644 --- a/src/components/mail/listing.rs +++ b/src/components/mail/listing.rs @@ -576,8 +576,9 @@ impl Component for Listing { { /* Account might have no mailboxes yet if it's offline */ /* Check if per-mailbox configuration overrides general configuration */ - let index_style = - mailbox_acc_settings!(context[self.cursor_pos.0][mailbox_hash].index_style); + let index_style = mailbox_settings!( + context[self.cursor_pos.0][mailbox_hash].listing.index_style + ); self.component.set_style(*index_style); } context @@ -1200,7 +1201,7 @@ impl Listing { /* Check if per-mailbox configuration overrides general configuration */ let index_style = - mailbox_acc_settings!(context[self.cursor_pos.0][mailbox_hash].index_style); + mailbox_settings!(context[self.cursor_pos.0][mailbox_hash].listing.index_style); self.component.set_style(*index_style); } else { /* Set to dummy */ diff --git a/src/components/mail/listing/compact.rs b/src/components/mail/listing/compact.rs index ac626aea..06bcc1aa 100644 --- a/src/components/mail/listing/compact.rs +++ b/src/components/mail/listing/compact.rs @@ -650,31 +650,27 @@ impl CompactListing { hash: ThreadHash, ) -> EntryStrings { let thread = threads.thread_ref(hash); - let mailbox = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf; let mut tags = String::new(); let mut colors: SmallVec<[_; 8]> = SmallVec::new(); let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap(); if let Some(t) = backend_lck.tags() { let tags_lck = t.read().unwrap(); for t in e.labels().iter() { - if mailbox - .conf_override - .tags - .as_ref() - .map(|s| s.ignore_tags.contains(t)) - .unwrap_or(false) + if mailbox_settings!( + context[self.cursor_pos.0][&self.cursor_pos.1] + .tags + .ignore_tags + ) + .contains(t) { continue; } tags.push(' '); tags.push_str(tags_lck.get(t).as_ref().unwrap()); tags.push(' '); - if let Some(&c) = mailbox - .conf_override - .tags - .as_ref() - .map(|s| s.colors.get(t)) - .unwrap_or(None) + if let Some(&c) = + mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].tags.colors) + .get(t) { colors.push(c); } else { @@ -761,10 +757,12 @@ impl CompactListing { .collection .get_env(root_env_hash); use crate::cache::QueryTrait; - if let Some(filter_query) = - mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].listing) + if let Some(filter_query) = mailbox_settings!( + context[self.cursor_pos.0][&self.cursor_pos.1] + .listing .filter - .as_ref() + ) + .as_ref() { if !root_envelope.is_match(filter_query) { continue; diff --git a/src/components/mail/listing/conversations.rs b/src/components/mail/listing/conversations.rs index 921b38e1..77c54351 100644 --- a/src/components/mail/listing/conversations.rs +++ b/src/components/mail/listing/conversations.rs @@ -593,20 +593,28 @@ impl ConversationsListing { hash: ThreadHash, ) -> EntryStrings { let thread = threads.thread_ref(hash); - let settings = mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].tags); let mut tags = String::new(); let mut colors = SmallVec::new(); let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap(); if let Some(t) = backend_lck.tags() { let tags_lck = t.read().unwrap(); for t in e.labels().iter() { - if settings.ignore_tags.contains(t) { + if mailbox_settings!( + context[self.cursor_pos.0][&self.cursor_pos.1] + .tags + .ignore_tags + ) + .contains(t) + { continue; } tags.push(' '); tags.push_str(tags_lck.get(t).as_ref().unwrap()); tags.push(' '); - if let Some(&c) = settings.colors.get(t) { + if let Some(&c) = + mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].tags.colors) + .get(t) + { colors.push(c); } else { colors.push(Color::Byte(8)); @@ -698,10 +706,12 @@ impl ConversationsListing { .collection .get_env(root_env_hash); use crate::cache::QueryTrait; - if let Some(filter_query) = - mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].listing) + if let Some(filter_query) = mailbox_settings!( + context[self.cursor_pos.0][&self.cursor_pos.1] + .listing .filter - .as_ref() + ) + .as_ref() { if !root_envelope.is_match(filter_query) { continue; diff --git a/src/components/mail/listing/plain.rs b/src/components/mail/listing/plain.rs index becce36e..f244a141 100644 --- a/src/components/mail/listing/plain.rs +++ b/src/components/mail/listing/plain.rs @@ -612,21 +612,30 @@ impl PlainListing { id: ComponentId::new_v4(), } } + fn make_entry_string(&self, e: EnvelopeRef, context: &Context) -> EntryStrings { - let settings = mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].tags); let mut tags = String::new(); let mut colors = SmallVec::new(); let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap(); if let Some(t) = backend_lck.tags() { let tags_lck = t.read().unwrap(); for t in e.labels().iter() { - if settings.ignore_tags.contains(t) { + if mailbox_settings!( + context[self.cursor_pos.0][&self.cursor_pos.1] + .tags + .ignore_tags + ) + .contains(t) + { continue; } tags.push(' '); tags.push_str(tags_lck.get(t).as_ref().unwrap()); tags.push(' '); - if let Some(&c) = settings.colors.get(t) { + if let Some(&c) = + mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].tags.colors) + .get(t) + { colors.push(c); } else { colors.push(Color::Byte(8)); @@ -705,10 +714,12 @@ impl PlainListing { } let envelope: EnvelopeRef = context.accounts[self.cursor_pos.0].collection.get_env(i); use crate::cache::QueryTrait; - if let Some(filter_query) = - mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].listing) + if let Some(filter_query) = mailbox_settings!( + context[self.cursor_pos.0][&self.cursor_pos.1] + .listing .filter - .as_ref() + ) + .as_ref() { if !envelope.is_match(filter_query) { continue; diff --git a/src/components/mail/status.rs b/src/components/mail/status.rs index 20742fdb..5a8c3e65 100644 --- a/src/components/mail/status.rs +++ b/src/components/mail/status.rs @@ -290,7 +290,7 @@ impl StatusPanel { None, ); write_string_to_grid( - &a.runtime_settings.account().identity, + &a.settings.account().identity, &mut self.content, self.theme_default.fg, self.theme_default.bg, diff --git a/src/components/mail/view.rs b/src/components/mail/view.rs index 00764a78..ae69ca2f 100644 --- a/src/components/mail/view.rs +++ b/src/components/mail/view.rs @@ -161,10 +161,15 @@ impl MailView { Some(Box::new(move |a: &'closure Attachment, v: &mut Vec| { if a.content_type().is_text_html() { use std::io::Write; - let settings = - mailbox_settings!(context[self.coordinates.0][&self.coordinates.1].pager); + /* FIXME: duplication with view/html.rs */ - if let Some(filter_invocation) = settings.html_filter.as_ref() { + if let Some(filter_invocation) = mailbox_settings!( + context[self.coordinates.0][&self.coordinates.1] + .pager + .html_filter + ) + .as_ref() + { let parts = split_command!(filter_invocation); let (cmd, args) = (parts[0], &parts[1..]); let command_obj = Command::new(cmd) @@ -400,10 +405,11 @@ impl Component for MailView { self.headers_no = 0; let mut skip_header_ctr = self.headers_cursor; - let sticky = - mailbox_settings!(context[self.coordinates.0][&self.coordinates.1].pager) + let sticky = *mailbox_settings!( + context[self.coordinates.0][&self.coordinates.1] + .pager .headers_sticky - || height_p < height; + ) || height_p < height; let (_, mut y) = upper_left; macro_rules! print_header { ($($string:expr)+) => { @@ -573,9 +579,11 @@ impl Component for MailView { context .dirty_areas .push_back((upper_left, set_y(bottom_right, y + 3))); - if !mailbox_settings!(context[self.coordinates.0][&self.coordinates.1].pager) - .headers_sticky - { + if !*mailbox_settings!( + context[self.coordinates.0][&self.coordinates.1] + .pager + .headers_sticky + ) { let height_p = self.pager.size().1; let height = height!(area).saturating_sub(y).saturating_sub(1); @@ -644,9 +652,10 @@ impl Component for MailView { } ViewMode::Normal if mailbox_settings!( - context[self.coordinates.0][&self.coordinates.1].pager + context[self.coordinates.0][&self.coordinates.1] + .pager + .auto_choose_multipart_alternative ) - .auto_choose_multipart_alternative .is_true() && match body.content_type { ContentType::Multipart { @@ -813,10 +822,11 @@ impl Component for MailView { _ => match event { UIEvent::Input(ref key) if shortcut!(key == shortcuts[Pager::DESCRIPTION]["scroll_up"]) - && !mailbox_settings!( - context[self.coordinates.0][&self.coordinates.1].pager + && !*mailbox_settings!( + context[self.coordinates.0][&self.coordinates.1] + .pager + .headers_sticky ) - .headers_sticky && self.headers_cursor <= self.headers_no => { self.force_draw_headers = true; @@ -832,10 +842,11 @@ impl Component for MailView { } UIEvent::Input(ref key) if shortcut!(key == shortcuts[Pager::DESCRIPTION]["scroll_down"]) - && !mailbox_settings!( - context[self.coordinates.0][&self.coordinates.1].pager + && !*mailbox_settings!( + context[self.coordinates.0][&self.coordinates.1] + .pager + .headers_sticky ) - .headers_sticky && self.headers_cursor < self.headers_no => { self.force_draw_headers = true; diff --git a/src/conf.rs b/src/conf.rs index 3d0ff0d3..5e1f4521 100644 --- a/src/conf.rs +++ b/src/conf.rs @@ -46,10 +46,10 @@ pub use self::shortcuts::*; pub use self::tags::*; use self::default_vals::*; -use self::listing::ListingSettings; -use self::notifications::NotificationsSettings; +use self::listing::{ListingSettings, ListingSettingsOverride}; +use self::notifications::{NotificationsSettings, NotificationsSettingsOverride}; use self::terminal::TerminalSettings; -use crate::pager::PagerSettings; +use crate::pager::{PagerSettings, PagerSettingsOverride}; use crate::plugins::Plugin; use melib::conf::{AccountSettings, MailboxConf, ToggleFlag}; use melib::error::*; @@ -72,38 +72,87 @@ macro_rules! split_command { #[macro_export] macro_rules! mailbox_acc_settings { - ($context:ident[$account_idx:expr][$mailbox_path:expr].$field:ident) => {{ - $context.accounts[$account_idx][$mailbox_path] - .conf + ($context:ident[$account_idx:expr].$setting:ident.$field:ident) => {{ + $context.accounts[$account_idx] + .settings .conf_override + .$setting .$field .as_ref() - .unwrap_or(&$context.accounts[$account_idx].settings.conf.$field) + .unwrap_or(&$context.settings.$setting.$field) }}; } #[macro_export] macro_rules! mailbox_settings { - ($context:ident[$account_idx:expr][$mailbox_path:expr].$field:ident) => {{ + ($context:ident[$account_idx:expr][$mailbox_path:expr].$setting:ident.$field:ident) => {{ $context.accounts[$account_idx][$mailbox_path] .conf .conf_override + .$setting .$field .as_ref() - .unwrap_or(&$context.settings.$field) + .or($context.accounts[$account_idx] + .settings + .conf_override + .$setting + .$field + .as_ref()) + .unwrap_or(&$context.settings.$setting.$field) }}; } +#[macro_export] +macro_rules! override_def { + ($override_name:ident, + $(#[$outer:meta])* + pub struct $name:ident { $( $(#[$fouter:meta])* $fname:ident : $ft:ty),*, + }) => { + $(#[$outer])* + pub struct $name { + $( + $(#[$fouter])* + pub $fname : $ft + ),* + } + $(#[$outer])* + pub struct $override_name { + $( + $(#[$fouter])* + pub $fname : Option<$ft> + ),* + } + impl Default for $override_name { + fn default() -> Self { + $override_name { + $( + $fname : None + ),* + } + } + } + } +} + #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct MailUIConf { - pub pager: Option, - pub listing: Option, - pub notifications: Option, - pub shortcuts: Option, - pub composing: Option, + #[serde(default)] + pub pager: PagerSettingsOverride, + #[serde(default)] + pub listing: ListingSettingsOverride, + #[serde(default)] + pub notifications: NotificationsSettingsOverride, + #[serde(default)] + pub shortcuts: ShortcutsOverride, + #[serde(default)] + pub composing: ComposingSettingsOverride, + #[serde(default)] pub identity: Option, - pub index_style: Option, - pub tags: Option, + #[serde(default)] + pub tags: TagsSettingsOverride, + #[serde(default)] pub theme: Option, + #[serde(default)] + pub pgp: PGPSettingsOverride, } #[serde(default)] @@ -133,7 +182,6 @@ pub struct FileAccount { identity: String, #[serde(default = "none")] display_name: Option, - pub index_style: IndexStyle, #[serde(default = "false_val")] read_only: bool, @@ -148,6 +196,8 @@ pub struct FileAccount { #[serde(default = "none")] pub refresh_command: Option, #[serde(flatten)] + pub conf_override: MailUIConf, + #[serde(flatten)] #[serde(deserialize_with = "extra_settings")] pub extra: HashMap, /* use custom deserializer to convert any given value (eg bool, number, etc) to string */ } @@ -180,6 +230,7 @@ impl From for AccountConf { let mailbox_confs = x.mailboxes.clone(); AccountConf { account: acc, + conf_override: x.conf_override.clone(), conf: x, mailbox_confs, } @@ -195,10 +246,6 @@ impl FileAccount { &self.root_mailbox } - pub fn index_style(&self) -> IndexStyle { - self.index_style - } - pub fn cache_type(&self) -> &CacheType { &self.cache_type } @@ -230,6 +277,7 @@ pub struct FileSettings { pub struct AccountConf { pub(crate) account: AccountSettings, pub(crate) conf: FileAccount, + pub conf_override: MailUIConf, pub(crate) mailbox_confs: HashMap, } @@ -237,6 +285,9 @@ impl AccountConf { pub fn account(&self) -> &AccountSettings { &self.account } + pub fn account_mut(&mut self) -> &mut AccountSettings { + &mut self.account + } pub fn conf(&self) -> &FileAccount { &self.conf } @@ -375,8 +426,8 @@ impl FileSettings { extra, manual_refresh, refresh_command: _, - index_style: _, cache_type: _, + conf_override: _, } = acc.clone(); let lowercase_format = format.to_lowercase(); @@ -448,48 +499,48 @@ impl Default for IndexStyle { */ mod default_vals { - pub(in crate::conf) fn false_val() -> bool { - false + pub(in crate::conf) fn false_val>() -> T { + false.into() } - pub(in crate::conf) fn true_val() -> bool { - true + pub(in crate::conf) fn true_val>() -> T { + true.into() } - pub(in crate::conf) fn zero_val() -> usize { - 0 + pub(in crate::conf) fn zero_val>() -> T { + 0.into() } - pub(in crate::conf) fn eighty_percent() -> usize { - 80 + pub(in crate::conf) fn eighty_val>() -> T { + 80.into() } pub(in crate::conf) fn none() -> Option { None } - pub(in crate::conf) fn internal_value_false() -> super::ToggleFlag { - super::ToggleFlag::InternalVal(false) + pub(in crate::conf) fn internal_value_false>() -> T { + super::ToggleFlag::InternalVal(false).into() } - pub(in crate::conf) fn internal_value_true() -> super::ToggleFlag { - super::ToggleFlag::InternalVal(true) + pub(in crate::conf) fn internal_value_true>() -> T { + super::ToggleFlag::InternalVal(true).into() } } mod deserializers { use serde::{Deserialize, Deserializer}; - pub(in crate::conf) fn non_empty_string<'de, D>( + pub(in crate::conf) fn non_empty_string<'de, D, T: std::convert::From>>( deserializer: D, - ) -> std::result::Result, D::Error> + ) -> std::result::Result where D: Deserializer<'de>, { let s = ::deserialize(deserializer)?; if s.is_empty() { - Ok(None) + Ok(None.into()) } else { - Ok(Some(s)) + Ok(Some(s).into()) } } diff --git a/src/conf/accounts.rs b/src/conf/accounts.rs index d9e9595b..2d83efd5 100644 --- a/src/conf/accounts.rs +++ b/src/conf/accounts.rs @@ -122,7 +122,6 @@ pub struct Account { pub(crate) address_book: AddressBook, pub(crate) work_context: WorkContext, pub(crate) settings: AccountConf, - pub(crate) runtime_settings: AccountConf, pub(crate) backend: Arc>>, sender: Sender, @@ -242,7 +241,6 @@ impl Account { sent_mailbox: Default::default(), collection: Default::default(), work_context, - runtime_settings: settings.clone(), settings, backend: Arc::new(RwLock::new(backend)), notify_fn, diff --git a/src/conf/composing.rs b/src/conf/composing.rs index 89bf8c71..1846e472 100644 --- a/src/conf/composing.rs +++ b/src/conf/composing.rs @@ -21,29 +21,34 @@ //! Configuration for composing email. use super::default_vals::{false_val, none, true_val}; +use crate::override_def; use std::collections::HashMap; -/// Settings for writing and sending new e-mail -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ComposingSettings { - /// A command to pipe new emails to - /// Required - pub mailer_cmd: String, - /// Command to launch editor. Can have arguments. Draft filename is given as the last argument. If it's missing, the environment variable $EDITOR is looked up. - #[serde(default = "none")] - pub editor_cmd: Option, - /// Embed editor (for terminal interfaces) instead of forking and waiting. - #[serde(default = "false_val")] - pub embed: bool, - /// Set "format=flowed" in plain text attachments. - /// Default: true - #[serde(default = "true_val")] - pub format_flowed: bool, - /// Set default header values for new drafts - /// Default: empty - #[serde(default)] - pub default_header_values: HashMap, -} +override_def!( + ComposingSettingsOverride, + /// Settings for writing and sending new e-mail + #[derive(Debug, Serialize, Deserialize, Clone)] + pub struct ComposingSettings { + /// A command to pipe new emails to + /// Required + #[serde(alias = "mailer-cmd")] + mailer_cmd: String, + /// Command to launch editor. Can have arguments. Draft filename is given as the last argument. If it's missing, the environment variable $EDITOR is looked up. + #[serde(default = "none", alias = "editor-cmd")] + editor_cmd: Option, + /// Embed editor (for terminal interfaces) instead of forking and waiting. + #[serde(default = "false_val")] + embed: bool, + /// Set "format=flowed" in plain text attachments. + /// Default: true + #[serde(default = "true_val", alias = "format-flowed")] + format_flowed: bool, + /// Set default header values for new drafts + /// Default: empty + #[serde(default, alias = "default-header-values")] + default_header_values: HashMap, + } +); impl Default for ComposingSettings { fn default() -> Self { diff --git a/src/conf/listing.rs b/src/conf/listing.rs index a984eebf..b92eb60d 100644 --- a/src/conf/listing.rs +++ b/src/conf/listing.rs @@ -19,29 +19,48 @@ * along with meli. If not, see . */ -use super::default_vals::*; +use super::{default_vals::*, IndexStyle}; use crate::cache::Query; +use crate::override_def; -/// Settings for mail listings -#[derive(Debug, Deserialize, Clone, Default, Serialize)] -pub struct ListingSettings { - /// Number of context lines when going to next page. - /// Default: 0 - #[serde(default = "zero_val")] - pub context_lines: usize, +override_def!( + ListingSettingsOverride, + /// Settings for mail listings + #[derive(Debug, Deserialize, Clone, Serialize)] + pub struct ListingSettings { + /// Number of context lines when going to next page. + /// Default: 0 + #[serde(default = "zero_val", alias = "context-lines")] + context_lines: usize, - /// Datetime formatting passed verbatim to strftime(3). - /// Default: %Y-%m-%d %T - #[serde(default = "none")] - pub datetime_fmt: Option, + /// Datetime formatting passed verbatim to strftime(3). + /// Default: %Y-%m-%d %T + #[serde(default = "none", alias = "datetime-fmt")] + datetime_fmt: Option, - /// Show recent dates as `X {minutes,hours,days} ago`, up to 7 days. - /// Default: true - #[serde(default = "true_val")] - pub recent_dates: bool, + /// Show recent dates as `X {minutes,hours,days} ago`, up to 7 days. + /// Default: true + #[serde(default = "true_val", alias = "recent-dates")] + recent_dates: bool, - /// Show only envelopes that match this query - /// Default: None - #[serde(default = "none")] - pub filter: Option, + /// Show only envelopes that match this query + /// Default: None + #[serde(default = "none")] + filter: Option, + + #[serde(default, alias = "index-style")] + index_style: IndexStyle, + } +); + +impl Default for ListingSettings { + fn default() -> Self { + Self { + context_lines: 0, + datetime_fmt: None, + recent_dates: true, + filter: None, + index_style: IndexStyle::default(), + } + } } diff --git a/src/conf/notifications.rs b/src/conf/notifications.rs index 028d4fa9..70182f53 100644 --- a/src/conf/notifications.rs +++ b/src/conf/notifications.rs @@ -19,26 +19,37 @@ * along with meli. If not, see . */ -use super::default_vals::internal_value_false; +use super::default_vals::{internal_value_false, none}; +use crate::override_def; -fn none() -> Option { - None -} +override_def!( + NotificationsSettingsOverride, + /// Settings for the notifications function. + #[derive(Debug, Serialize, Deserialize, Clone)] + pub struct NotificationsSettings { + /// A command to pipe notifications through + /// Default: None + #[serde(default = "none")] + script: Option, + /// A file location which has its size changed when new mail arrives (max 128 bytes). Can be + /// used to trigger new mail notifications eg with `xbiff(1)` + /// Default: None + #[serde(default = "none", alias = "xbiff-file-path")] + xbiff_file_path: Option, + #[serde(default = "internal_value_false", alias = "play-sound")] + play_sound: super::ToggleFlag, + #[serde(default = "none", alias = "sound-file")] + sound_file: Option, + } +); -/// Settings for the notifications function. -#[derive(Debug, Serialize, Deserialize, Clone, Default)] -pub struct NotificationsSettings { - /// A command to pipe notifications through - /// Default: None - #[serde(default = "none")] - pub script: Option, - /// A file location which has its size changed when new mail arrives (max 128 bytes). Can be - /// used to trigger new mail notifications eg with `xbiff(1)` - /// Default: None - #[serde(default = "none")] - pub xbiff_file_path: Option, - #[serde(default = "internal_value_false")] - pub play_sound: super::ToggleFlag, - #[serde(default = "none")] - pub sound_file: Option, +impl Default for NotificationsSettings { + fn default() -> Self { + Self { + script: None, + xbiff_file_path: None, + play_sound: super::ToggleFlag::InternalVal(false), + sound_file: None, + } + } } diff --git a/src/conf/pager.rs b/src/conf/pager.rs index fe41e8b4..0f658709 100644 --- a/src/conf/pager.rs +++ b/src/conf/pager.rs @@ -23,63 +23,87 @@ use super::default_vals::*; use super::deserializers::*; +use crate::override_def; use melib::ToggleFlag; -/// Settings for the pager function. -#[derive(Debug, Deserialize, Clone, Default, Serialize)] -pub struct PagerSettings { - /// Number of context lines when going to next page. - /// Default: 0 - #[serde(default = "zero_val")] - pub pager_context: usize, +override_def!( + PagerSettingsOverride, + /// Settings for the pager function. + #[derive(Debug, Deserialize, Clone, Serialize)] + pub struct PagerSettings { + /// Number of context lines when going to next page. + /// Default: 0 + #[serde(default = "zero_val", alias = "pager-context")] + pager_context: usize, - /// Stop at the end instead of displaying next mail. - /// Default: false - #[serde(default = "false_val")] - pub pager_stop: bool, + /// Stop at the end instead of displaying next mail. + /// Default: false + #[serde(default = "false_val", alias = "pager-stop")] + pager_stop: bool, - /// Always show headers when scrolling. - /// Default: true - #[serde(default = "true_val")] - pub headers_sticky: bool, + /// Always show headers when scrolling. + /// Default: true + #[serde(default = "true_val", alias = "headers-sticky")] + headers_sticky: bool, - /// The height of the pager in mail view, in percent. - /// Default: 80 - #[serde(default = "eighty_percent")] - pub pager_ratio: usize, + /// The height of the pager in mail view, in percent. + /// Default: 80 + #[serde(default = "eighty_val", alias = "pager-ratio")] + pager_ratio: usize, - /// A command to pipe mail output through for viewing in pager. - /// Default: None - #[serde(default = "none", deserialize_with = "non_empty_string")] - pub filter: Option, + /// A command to pipe mail output through for viewing in pager. + /// Default: None + #[serde(default = "none", deserialize_with = "non_empty_string")] + filter: Option, - /// A command to pipe html output before displaying it in a pager - /// Default: None - #[serde(default = "none", deserialize_with = "non_empty_string")] - pub html_filter: Option, + /// A command to pipe html output before displaying it in a pager + /// Default: None + #[serde( + default = "none", + deserialize_with = "non_empty_string", + alias = "html-filter" + )] + html_filter: Option, - /// Respect "format=flowed" - /// Default: true - #[serde(default = "true_val")] - pub format_flowed: bool, + /// Respect "format=flowed" + /// Default: true + #[serde(default = "true_val", alias = "format-flowed")] + format_flowed: bool, - /// Split long lines that would overflow on the x axis. - /// Default: true - #[serde(default = "true_val")] - pub split_long_lines: bool, + /// Split long lines that would overflow on the x axis. + /// Default: true + #[serde(default = "true_val", alias = "split-long-lines")] + split_long_lines: bool, - /// Minimum text width in columns. - /// Default: 80 - #[serde(default = "eighty_val")] - pub minimum_width: usize, + /// Minimum text width in columns. + /// Default: 80 + #[serde(default = "eighty_val", alias = "minimum-width")] + minimum_width: usize, - /// Choose `text/html` alternative if `text/plain` is empty in `multipart/alternative` - /// attachments. - /// Default: true - #[serde(default = "internal_value_true")] - pub auto_choose_multipart_alternative: ToggleFlag, -} - -fn eighty_val() -> usize { - 80 + /// Choose `text/html` alternative if `text/plain` is empty in `multipart/alternative` + /// attachments. + /// Default: true + #[serde( + default = "internal_value_true", + alias = "auto-choose-multipart-alternative" + )] + auto_choose_multipart_alternative: ToggleFlag, + } +); + +impl Default for PagerSettings { + fn default() -> Self { + Self { + pager_context: 0, + pager_stop: false, + headers_sticky: true, + pager_ratio: 80, + filter: None, + html_filter: None, + format_flowed: true, + split_long_lines: true, + minimum_width: 80, + auto_choose_multipart_alternative: ToggleFlag::InternalVal(true), + } + } } diff --git a/src/conf/pgp.rs b/src/conf/pgp.rs index f7c59e1f..c6c00c78 100644 --- a/src/conf/pgp.rs +++ b/src/conf/pgp.rs @@ -20,26 +20,30 @@ */ use super::default_vals::*; +use crate::override_def; -/// Settings for digital signing and encryption -#[derive(Debug, Deserialize, Clone, Serialize)] -pub struct PGPSettings { - /// auto verify signed e-mail according to RFC3156 - #[serde(default = "true_val")] - pub auto_verify_signatures: bool, +override_def!( + PGPSettingsOverride, + /// Settings for digital signing and encryption + #[derive(Debug, Deserialize, Clone, Serialize)] + pub struct PGPSettings { + /// auto verify signed e-mail according to RFC3156 + #[serde(default = "true_val", alias = "auto-verify-signatures")] + auto_verify_signatures: bool, - /// always sign sent messages - #[serde(default = "false_val")] - pub auto_sign: bool, + /// always sign sent messages + #[serde(default = "false_val", alias = "auto-sign")] + auto_sign: bool, - // https://tools.ietf.org/html/rfc4880#section-12.2 - #[serde(default = "none")] - pub key: Option, + // https://tools.ietf.org/html/rfc4880#section-12.2 + #[serde(default = "none")] + key: Option, - /// gpg binary name or file location to use - #[serde(default)] - pub gpg_binary: Option, -} + /// gpg binary name or file location to use + #[serde(default, alias = "gpg-binary")] + gpg_binary: Option, + } +); impl Default for PGPSettings { fn default() -> Self { diff --git a/src/conf/shortcuts.rs b/src/conf/shortcuts.rs index cd77f647..03b6eb7d 100644 --- a/src/conf/shortcuts.rs +++ b/src/conf/shortcuts.rs @@ -19,6 +19,7 @@ * along with meli. If not, see . */ +use crate::override_def; use crate::terminal::Key; use fnv::FnvHashMap; @@ -32,24 +33,42 @@ macro_rules! shortcut { }; } -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct Shortcuts { - #[serde(default)] - pub general: GeneralShortcuts, - #[serde(default)] - pub listing: ListingShortcuts, - #[serde(default)] - pub composing: ComposingShortcuts, - #[serde(default, alias = "compact-listing")] - pub compact_listing: CompactListingShortcuts, - #[serde(default, alias = "contact-list")] - pub contact_list: ContactListShortcuts, - #[serde(default, alias = "envelope-view")] - pub envelope_view: EnvelopeViewShortcuts, - #[serde(default, alias = "thread-view")] - pub thread_view: ThreadViewShortcuts, - #[serde(default)] - pub pager: PagerShortcuts, +override_def!( + ShortcutsOverride, + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct Shortcuts { + #[serde(default)] + general: GeneralShortcuts, + #[serde(default)] + listing: ListingShortcuts, + #[serde(default)] + composing: ComposingShortcuts, + #[serde(default, alias = "compact-listing")] + compact_listing: CompactListingShortcuts, + #[serde(default, alias = "contact-list")] + contact_list: ContactListShortcuts, + #[serde(default, alias = "envelope-view")] + envelope_view: EnvelopeViewShortcuts, + #[serde(default, alias = "thread-view")] + thread_view: ThreadViewShortcuts, + #[serde(default)] + pager: PagerShortcuts, + } +); + +impl Default for Shortcuts { + fn default() -> Self { + Self { + general: GeneralShortcuts::default(), + listing: ListingShortcuts::default(), + composing: ComposingShortcuts::default(), + compact_listing: CompactListingShortcuts::default(), + contact_list: ContactListShortcuts::default(), + envelope_view: EnvelopeViewShortcuts::default(), + thread_view: ThreadViewShortcuts::default(), + pager: PagerShortcuts::default(), + } + } } /// Create a struct holding all of a Component's shortcuts. diff --git a/src/conf/tags.rs b/src/conf/tags.rs index 72d94b7c..baa464f0 100644 --- a/src/conf/tags.rs +++ b/src/conf/tags.rs @@ -21,18 +21,22 @@ //! E-mail tag configuration and {de,}serializing. +use crate::override_def; use crate::terminal::Color; use serde::{Deserialize, Deserializer}; use std::collections::{hash_map::DefaultHasher, HashMap, HashSet}; use std::hash::Hasher; -#[derive(Debug, Deserialize, Clone, Serialize)] -pub struct TagsSettings { - #[serde(default, deserialize_with = "tag_color_de")] - pub colors: HashMap, - #[serde(default, deserialize_with = "tag_set_de")] - pub ignore_tags: HashSet, -} +override_def!( + TagsSettingsOverride, + #[derive(Debug, Deserialize, Clone, Serialize)] + pub struct TagsSettings { + #[serde(default, deserialize_with = "tag_color_de")] + colors: HashMap, + #[serde(default, deserialize_with = "tag_set_de", alias = "ignore-tags")] + ignore_tags: HashSet, + } +); impl Default for TagsSettings { fn default() -> Self { @@ -43,7 +47,9 @@ impl Default for TagsSettings { } } -pub fn tag_set_de<'de, D>(deserializer: D) -> std::result::Result, D::Error> +pub fn tag_set_de<'de, D, T: std::convert::From>>( + deserializer: D, +) -> std::result::Result where D: Deserializer<'de>, { @@ -54,10 +60,13 @@ where hasher.write(tag.as_bytes()); hasher.finish() }) - .collect()) + .collect::>() + .into()) } -pub fn tag_color_de<'de, D>(deserializer: D) -> std::result::Result, D::Error> +pub fn tag_color_de<'de, D, T: std::convert::From>>( + deserializer: D, +) -> std::result::Result where D: Deserializer<'de>, { @@ -81,5 +90,6 @@ where }, ) }) - .collect()) + .collect::>() + .into()) }