diff --git a/melib/src/conf.rs b/melib/src/conf.rs index 55d2a5e2..36fdb969 100644 --- a/melib/src/conf.rs +++ b/melib/src/conf.rs @@ -18,6 +18,8 @@ * You should have received a copy of the GNU General Public License * along with meli. If not, see . */ +use crate::backends::SpecialUseMailbox; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::collections::hash_map::HashMap; #[derive(Debug, Serialize, Default, Clone)] @@ -29,6 +31,8 @@ pub struct AccountSettings { pub read_only: bool, pub display_name: Option, pub subscribed_folders: Vec, + #[serde(default)] + pub folders: HashMap, #[serde(flatten)] pub extra: HashMap, } @@ -60,3 +64,114 @@ impl AccountSettings { &self.subscribed_folders } } + +#[serde(default)] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FolderConf { + pub rename: Option, + #[serde(default = "true_val")] + pub autoload: bool, + #[serde(deserialize_with = "toggleflag_de")] + pub subscribe: ToggleFlag, + #[serde(deserialize_with = "toggleflag_de")] + pub ignore: ToggleFlag, + #[serde(default = "none")] + pub usage: Option, + #[serde(flatten)] + pub extra: HashMap, +} + +impl Default for FolderConf { + fn default() -> Self { + FolderConf { + rename: None, + autoload: true, + subscribe: ToggleFlag::Unset, + ignore: ToggleFlag::Unset, + usage: None, + extra: HashMap::default(), + } + } +} + +impl FolderConf { + pub fn rename(&self) -> Option<&str> { + self.rename.as_ref().map(String::as_str) + } +} + +pub(in crate::conf) fn true_val() -> bool { + true +} + +pub(in crate::conf) fn none() -> Option { + None +} + +#[derive(Copy, Debug, Clone, PartialEq)] +pub enum ToggleFlag { + Unset, + InternalVal(bool), + False, + True, +} + +impl From for ToggleFlag { + fn from(val: bool) -> Self { + if val { + ToggleFlag::True + } else { + ToggleFlag::False + } + } +} + +impl Default for ToggleFlag { + fn default() -> Self { + ToggleFlag::Unset + } +} + +impl ToggleFlag { + pub fn is_unset(&self) -> bool { + ToggleFlag::Unset == *self + } + pub fn is_internal(&self) -> bool { + if let ToggleFlag::InternalVal(_) = *self { + true + } else { + false + } + } + pub fn is_false(&self) -> bool { + ToggleFlag::False == *self || ToggleFlag::InternalVal(false) == *self + } + pub fn is_true(&self) -> bool { + ToggleFlag::True == *self || ToggleFlag::InternalVal(true) == *self + } +} + +pub fn toggleflag_de<'de, D>(deserializer: D) -> std::result::Result +where + D: Deserializer<'de>, +{ + let s = ::deserialize(deserializer); + Ok(match s { + Err(_) => ToggleFlag::Unset, + Ok(true) => ToggleFlag::True, + Ok(false) => ToggleFlag::False, + }) +} + +impl Serialize for ToggleFlag { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + match self { + ToggleFlag::Unset | ToggleFlag::InternalVal(_) => serializer.serialize_none(), + ToggleFlag::False => serializer.serialize_bool(false), + ToggleFlag::True => serializer.serialize_bool(true), + } + } +} diff --git a/ui/src/conf.rs b/ui/src/conf.rs index 5cde9eeb..79c9b113 100644 --- a/ui/src/conf.rs +++ b/ui/src/conf.rs @@ -42,7 +42,7 @@ use self::notifications::NotificationsSettings; use self::terminal::TerminalSettings; use crate::pager::PagerSettings; use melib::backends::SpecialUseMailbox; -use melib::conf::AccountSettings; +use melib::conf::{toggleflag_de, AccountSettings, FolderConf, ToggleFlag}; use melib::error::*; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; @@ -60,49 +60,6 @@ macro_rules! split_command { }}; } -#[derive(Copy, Debug, Clone, PartialEq)] -pub enum ToggleFlag { - Unset, - InternalVal(bool), - False, - True, -} - -impl From for ToggleFlag { - fn from(val: bool) -> Self { - if val { - ToggleFlag::True - } else { - ToggleFlag::False - } - } -} - -impl Default for ToggleFlag { - fn default() -> Self { - ToggleFlag::Unset - } -} - -impl ToggleFlag { - pub fn is_unset(&self) -> bool { - ToggleFlag::Unset == *self - } - pub fn is_internal(&self) -> bool { - if let ToggleFlag::InternalVal(_) = *self { - true - } else { - false - } - } - pub fn is_false(&self) -> bool { - ToggleFlag::False == *self || ToggleFlag::InternalVal(false) == *self - } - pub fn is_true(&self) -> bool { - ToggleFlag::True == *self || ToggleFlag::InternalVal(true) == *self - } -} - #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct MailUIConf { pub pager: Option, @@ -114,37 +71,21 @@ pub struct MailUIConf { } #[serde(default)] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct FolderConf { - pub rename: Option, - #[serde(default = "true_val")] - pub autoload: bool, - #[serde(deserialize_with = "toggleflag_de")] - pub subscribe: ToggleFlag, - #[serde(deserialize_with = "toggleflag_de")] - pub ignore: ToggleFlag, - #[serde(default = "none")] - pub usage: Option, +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct FileFolderConf { + #[serde(flatten)] + pub folder_conf: FolderConf, #[serde(flatten)] pub conf_override: MailUIConf, } -impl Default for FolderConf { - fn default() -> Self { - FolderConf { - rename: None, - autoload: true, - subscribe: ToggleFlag::Unset, - ignore: ToggleFlag::Unset, - usage: None, - conf_override: MailUIConf::default(), - } +impl FileFolderConf { + pub fn conf_override(&self) -> &MailUIConf { + &self.conf_override } -} -impl FolderConf { - pub fn rename(&self) -> Option<&str> { - self.rename.as_ref().map(String::as_str) + pub fn folder_conf(&self) -> &FolderConf { + &self.folder_conf } } @@ -165,7 +106,8 @@ pub struct FileAccount { #[serde(default = "false_val")] read_only: bool, subscribed_folders: Vec, - folders: Option>, + #[serde(default)] + folders: HashMap, #[serde(default)] cache_type: CacheType, } @@ -176,6 +118,11 @@ impl From for AccountConf { let root_folder = x.root_folder.clone(); let identity = x.identity.clone(); let display_name = x.display_name.clone(); + let folders = x + .folders + .iter() + .map(|(k, v)| (k.clone(), v.folder_conf.clone())) + .collect(); let mut acc = AccountSettings { name: String::new(), @@ -185,6 +132,7 @@ impl From for AccountConf { read_only: x.read_only, display_name, subscribed_folders: x.subscribed_folders.clone(), + folders, extra: x.extra.clone(), }; @@ -200,52 +148,58 @@ impl From for AccountConf { if !acc.subscribed_folders.contains(&root_tmp) { acc.subscribed_folders.push(root_tmp); } - let mut folder_confs = x.folders.clone().unwrap_or_else(Default::default); + let mut folder_confs = x.folders.clone(); for s in &x.subscribed_folders { if !folder_confs.contains_key(s) { folder_confs.insert( s.to_string(), - FolderConf { - subscribe: ToggleFlag::True, - ..FolderConf::default() + FileFolderConf { + folder_conf: FolderConf { + subscribe: ToggleFlag::True, + ..FolderConf::default() + }, + ..FileFolderConf::default() }, ); } else { - if !folder_confs[s].subscribe.is_unset() { - eprintln!("Configuration error: folder `{}` cannot both have `subscribe` flag set and be in the `subscribed_folders` array", s); - std::process::exit(1); + if !folder_confs[s].folder_conf().subscribe.is_unset() { + continue; } - folder_confs.get_mut(s).unwrap().subscribe = ToggleFlag::True; + folder_confs.get_mut(s).unwrap().folder_conf.subscribe = ToggleFlag::True; } - if folder_confs[s].usage.is_none() { + if folder_confs[s].folder_conf().usage.is_none() { let name = s .split(if s.contains('/') { '/' } else { '.' }) .last() .unwrap_or(""); - folder_confs.get_mut(s).unwrap().usage = if name.eq_ignore_ascii_case("inbox") { - Some(SpecialUseMailbox::Inbox) - } else if name.eq_ignore_ascii_case("archive") { - Some(SpecialUseMailbox::Archive) - } else if name.eq_ignore_ascii_case("drafts") { - Some(SpecialUseMailbox::Drafts) - } else if name.eq_ignore_ascii_case("junk") { - Some(SpecialUseMailbox::Junk) - } else if name.eq_ignore_ascii_case("spam") { - Some(SpecialUseMailbox::Junk) - } else if name.eq_ignore_ascii_case("sent") { - Some(SpecialUseMailbox::Sent) - } else if name.eq_ignore_ascii_case("trash") { - Some(SpecialUseMailbox::Trash) - } else { - Some(SpecialUseMailbox::Normal) - }; + folder_confs.get_mut(s).unwrap().folder_conf.usage = + if name.eq_ignore_ascii_case("inbox") { + Some(SpecialUseMailbox::Inbox) + } else if name.eq_ignore_ascii_case("archive") { + Some(SpecialUseMailbox::Archive) + } else if name.eq_ignore_ascii_case("drafts") { + Some(SpecialUseMailbox::Drafts) + } else if name.eq_ignore_ascii_case("junk") { + Some(SpecialUseMailbox::Junk) + } else if name.eq_ignore_ascii_case("spam") { + Some(SpecialUseMailbox::Junk) + } else if name.eq_ignore_ascii_case("sent") { + Some(SpecialUseMailbox::Sent) + } else if name.eq_ignore_ascii_case("trash") { + Some(SpecialUseMailbox::Trash) + } else { + Some(SpecialUseMailbox::Normal) + }; } - if folder_confs[s].ignore.is_unset() { + if folder_confs[s].folder_conf().ignore.is_unset() { use SpecialUseMailbox::*; - if [Junk, Sent, Trash].contains(&folder_confs[s].usage.as_ref().unwrap()) { - folder_confs.get_mut(s).unwrap().ignore = ToggleFlag::InternalVal(true); + if [Junk, Sent, Trash] + .contains(&folder_confs[s].folder_conf().usage.as_ref().unwrap()) + { + folder_confs.get_mut(s).unwrap().folder_conf.ignore = + ToggleFlag::InternalVal(true); } } } @@ -259,8 +213,8 @@ impl From for AccountConf { } impl FileAccount { - pub fn folders(&self) -> Option<&HashMap> { - self.folders.as_ref() + pub fn folders(&self) -> &HashMap { + &self.folders } pub fn folder(&self) -> &str { @@ -296,7 +250,7 @@ struct FileSettings { pub struct AccountConf { pub(crate) account: AccountSettings, pub(crate) conf: FileAccount, - pub(crate) folder_confs: HashMap, + pub(crate) folder_confs: HashMap, } impl AccountConf { @@ -427,31 +381,6 @@ impl Default for IndexStyle { } } -fn toggleflag_de<'de, D>(deserializer: D) -> std::result::Result -where - D: Deserializer<'de>, -{ - let s = ::deserialize(deserializer); - Ok(match s { - Err(_) => ToggleFlag::Unset, - Ok(true) => ToggleFlag::True, - Ok(false) => ToggleFlag::False, - }) -} - -impl Serialize for ToggleFlag { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: Serializer, - { - match self { - ToggleFlag::Unset | ToggleFlag::InternalVal(_) => serializer.serialize_none(), - ToggleFlag::False => serializer.serialize_bool(false), - ToggleFlag::True => serializer.serialize_bool(true), - } - } -} - /* * Deserialize default functions */ diff --git a/ui/src/conf/accounts.rs b/ui/src/conf/accounts.rs index bede2a13..af859536 100644 --- a/ui/src/conf/accounts.rs +++ b/ui/src/conf/accounts.rs @@ -23,7 +23,7 @@ * Account management from user configuration. */ -use super::{AccountConf, FolderConf}; +use super::{AccountConf, FileFolderConf}; use fnv::FnvHashMap; use melib::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext}; use melib::backends::{ @@ -144,7 +144,7 @@ pub struct Account { name: String, pub is_online: bool, pub(crate) folders: FnvHashMap, - pub(crate) folder_confs: FnvHashMap, + pub(crate) folder_confs: FnvHashMap, pub(crate) folders_order: Vec, pub(crate) folder_names: FnvHashMap, tree: Vec, @@ -232,7 +232,8 @@ impl Account { let backend = map.get(settings.account().format())( settings.account(), Box::new(move |path: &str| { - s.folder_confs.contains_key(path) && s.folder_confs[path].subscribe.is_true() + s.folder_confs.contains_key(path) + && s.folder_confs[path].folder_conf().subscribe.is_true() }), ); let notify_fn = Arc::new(notify_fn); @@ -293,13 +294,16 @@ impl Account { let mut sent_folder = None; for f in ref_folders.values_mut() { if !self.settings.folder_confs.contains_key(f.path()) - || self.settings.folder_confs[f.path()].subscribe.is_false() + || self.settings.folder_confs[f.path()] + .folder_conf() + .subscribe + .is_false() { /* Skip unsubscribed folder */ continue; } - match self.settings.folder_confs[f.path()].usage { + match self.settings.folder_confs[f.path()].folder_conf().usage { Some(SpecialUseMailbox::Sent) => { sent_folder = Some(f.hash()); } @@ -314,7 +318,10 @@ impl Account { let mut collection: Collection = Collection::new(Default::default()); for (h, f) in ref_folders.iter() { if !self.settings.folder_confs.contains_key(f.path()) - || self.settings.folder_confs[f.path()].subscribe.is_false() + || self.settings.folder_confs[f.path()] + .folder_conf() + .subscribe + .is_false() { /* Skip unsubscribed folder */ continue; @@ -392,7 +399,7 @@ impl Account { let mut builder = AsyncBuilder::new(); let our_tx = builder.tx(); let folder_hash = folder.hash(); - let priority = match settings.folder_confs[folder.path()].usage { + let priority = match settings.folder_confs[folder.path()].folder_conf().usage { Some(SpecialUseMailbox::Inbox) => 0, Some(SpecialUseMailbox::Sent) => 1, Some(SpecialUseMailbox::Drafts) | Some(SpecialUseMailbox::Trash) => 2, @@ -563,7 +570,7 @@ impl Account { let ref_folders: FnvHashMap = self.backend.read().unwrap().folders(); let folder_conf = &self.settings.folder_confs[&self.folder_names[&folder_hash]]; - if folder_conf.ignore.is_true() { + if folder_conf.folder_conf().ignore.is_true() { return Some(UIEvent::MailboxUpdate((self.index, folder_hash))); } @@ -673,13 +680,12 @@ impl Account { } pub fn list_folders(&self) -> Vec { let mut folders = self.backend.read().unwrap().folders(); - if let Some(folder_confs) = self.settings.conf().folders() { - //debug!("folder renames: {:?}", folder_renames); - for f in folders.values_mut() { - if let Some(r) = folder_confs.get(f.path()) { - if let Some(rename) = r.rename() { - f.change_name(rename); - } + let folder_confs = self.settings.conf().folders(); + //debug!("folder renames: {:?}", folder_renames); + for f in folders.values_mut() { + if let Some(r) = folder_confs.get(f.path()) { + if let Some(rename) = r.folder_conf().rename() { + f.change_name(rename); } } } @@ -829,7 +835,7 @@ impl Account { self.backend.write().unwrap().folder_operation(path, op) } - pub fn folder_confs(&self, folder_hash: FolderHash) -> &FolderConf { + pub fn folder_confs(&self, folder_hash: FolderHash) -> &FileFolderConf { &self.folder_confs[&folder_hash] } @@ -838,7 +844,7 @@ impl Account { .settings .folder_confs .iter() - .find(|(_, f)| f.usage == Some(SpecialUseMailbox::Sent)); + .find(|(_, f)| f.folder_conf().usage == Some(SpecialUseMailbox::Sent)); if let Some(sent_folder) = sent_folder.as_ref() { sent_folder.0 } else { @@ -851,7 +857,7 @@ impl Account { .settings .folder_confs .iter() - .find(|(_, f)| f.usage == Some(special_use)); + .find(|(_, f)| f.folder_conf().usage == Some(special_use)); ret.as_ref().map(|r| r.0.as_str()) }