2018-08-19 13:12:48 +03:00
/*
* meli - configuration module .
*
* Copyright 2017 Manos Pitsidianakis
*
* This file is part of meli .
*
* meli is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* meli is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with meli . If not , see < http ://www.gnu.org/licenses/>.
* /
2020-02-04 15:52:12 +02:00
/*! Configuration logic and `config.toml` interfaces. */
2019-03-14 12:19:25 +02:00
extern crate bincode ;
2018-08-19 13:12:48 +03:00
extern crate serde ;
2019-08-25 14:14:54 +03:00
extern crate toml ;
2018-08-19 13:12:48 +03:00
extern crate xdg ;
2019-02-15 09:06:42 +02:00
2019-09-27 12:48:48 +03:00
pub mod composing ;
2019-03-02 21:40:57 +02:00
pub mod notifications ;
2019-03-14 12:19:25 +02:00
pub mod pager ;
2019-09-26 12:10:36 +03:00
pub mod pgp ;
2019-12-01 12:05:14 +02:00
pub mod tags ;
2019-11-28 22:16:56 +02:00
#[ macro_use ]
2019-03-03 14:24:15 +02:00
pub mod shortcuts ;
2020-01-08 21:41:57 +02:00
mod listing ;
2019-10-06 10:58:47 +03:00
pub mod terminal ;
2020-01-19 14:43:36 +02:00
mod themes ;
pub use themes ::* ;
2018-08-19 13:12:48 +03:00
2019-02-10 18:26:49 +02:00
pub mod accounts ;
pub use self ::accounts ::Account ;
2019-09-27 12:48:48 +03:00
pub use self ::composing ::* ;
2019-09-26 12:10:36 +03:00
pub use self ::pgp ::* ;
2019-03-14 12:19:25 +02:00
pub use self ::shortcuts ::* ;
2019-12-01 12:05:14 +02:00
pub use self ::tags ::* ;
2019-02-10 18:26:49 +02:00
2019-04-06 00:30:06 +03:00
use self ::default_vals ::* ;
2020-03-18 19:13:07 +02:00
use self ::listing ::{ ListingSettings , ListingSettingsOverride } ;
use self ::notifications ::{ NotificationsSettings , NotificationsSettingsOverride } ;
2019-10-06 10:58:47 +03:00
use self ::terminal ::TerminalSettings ;
2020-03-18 19:13:07 +02:00
use crate ::pager ::{ PagerSettings , PagerSettingsOverride } ;
2019-12-23 17:08:57 +02:00
use crate ::plugins ::Plugin ;
2020-02-26 10:54:10 +02:00
use melib ::conf ::{ AccountSettings , MailboxConf , ToggleFlag } ;
2018-08-30 15:54:30 +03:00
use melib ::error ::* ;
2018-08-19 13:12:48 +03:00
2019-08-25 21:44:19 +03:00
use serde ::{ de , Deserialize , Deserializer , Serialize , Serializer } ;
2018-08-19 13:12:48 +03:00
use std ::collections ::HashMap ;
2018-09-05 08:22:10 +03:00
use std ::env ;
2019-11-27 22:13:14 +02:00
use std ::fs ::OpenOptions ;
use std ::io ::{ self , BufRead , Write } ;
2019-11-24 17:00:55 +02:00
use std ::os ::unix ::fs ::PermissionsExt ;
use std ::path ::{ Path , PathBuf } ;
2018-09-05 08:22:10 +03:00
2019-04-06 00:30:06 +03:00
#[ macro_export ]
macro_rules ! split_command {
( $cmd :expr ) = > { {
$cmd . split_whitespace ( ) . collect ::< Vec < & str > > ( )
} } ;
2018-09-05 08:22:10 +03:00
}
2020-03-01 22:51:58 +02:00
#[ macro_export ]
macro_rules ! mailbox_acc_settings {
2020-03-18 19:13:07 +02:00
( $context :ident [ $account_idx :expr ] . $setting :ident . $field :ident ) = > { {
$context . accounts [ $account_idx ]
. settings
2020-03-01 22:51:58 +02:00
. conf_override
2020-03-18 19:13:07 +02:00
. $setting
2020-03-01 22:51:58 +02:00
. $field
. as_ref ( )
2020-03-18 19:13:07 +02:00
. unwrap_or ( & $context . settings . $setting . $field )
2020-03-01 22:51:58 +02:00
} } ;
}
#[ macro_export ]
macro_rules ! mailbox_settings {
2020-03-18 19:13:07 +02:00
( $context :ident [ $account_idx :expr ] [ $mailbox_path :expr ] . $setting :ident . $field :ident ) = > { {
2020-03-01 22:51:58 +02:00
$context . accounts [ $account_idx ] [ $mailbox_path ]
. conf
. conf_override
2020-03-18 19:13:07 +02:00
. $setting
2020-03-01 22:51:58 +02:00
. $field
. as_ref ( )
2020-03-18 19:13:07 +02:00
. or ( $context . accounts [ $account_idx ]
. settings
. conf_override
. $setting
. $field
. as_ref ( ) )
. unwrap_or ( & $context . settings . $setting . $field )
2020-03-01 22:51:58 +02:00
} } ;
}
2020-03-18 19:13:07 +02:00
#[ 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
) , *
}
}
}
}
}
2019-09-09 21:38:37 +03:00
#[ derive(Default, Debug, Clone, Serialize, Deserialize) ]
pub struct MailUIConf {
2020-03-18 19:13:07 +02:00
#[ 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) ]
2019-09-09 21:38:37 +03:00
pub identity : Option < String > ,
2020-03-18 19:13:07 +02:00
#[ serde(default) ]
pub tags : TagsSettingsOverride ,
#[ serde(default) ]
2020-06-02 02:47:02 +03:00
pub themes : Option < Themes > ,
2020-03-18 19:13:07 +02:00
#[ serde(default) ]
pub pgp : PGPSettingsOverride ,
2019-09-09 21:38:37 +03:00
}
2019-10-14 12:05:47 +03:00
#[ serde(default) ]
2019-11-15 19:51:42 +02:00
#[ derive(Debug, Default, Clone, Serialize, Deserialize) ]
2020-02-26 10:54:10 +02:00
pub struct FileMailboxConf {
2019-09-09 21:38:37 +03:00
#[ serde(flatten) ]
pub conf_override : MailUIConf ,
2019-12-01 22:29:31 +02:00
#[ serde(flatten) ]
2020-02-26 10:54:10 +02:00
pub mailbox_conf : MailboxConf ,
2019-05-13 21:08:18 +03:00
}
2020-02-26 10:54:10 +02:00
impl FileMailboxConf {
2019-11-15 19:51:42 +02:00
pub fn conf_override ( & self ) -> & MailUIConf {
& self . conf_override
2019-05-13 21:08:18 +03:00
}
2018-09-05 08:22:10 +03:00
2020-02-26 10:54:10 +02:00
pub fn mailbox_conf ( & self ) -> & MailboxConf {
& self . mailbox_conf
2018-09-05 08:22:10 +03:00
}
}
2018-08-19 13:12:48 +03:00
2019-10-24 18:16:41 +03:00
use crate ::conf ::deserializers ::extra_settings ;
2019-08-25 21:44:19 +03:00
#[ derive(Debug, Clone, Default, Serialize, Deserialize) ]
2018-08-19 13:12:48 +03:00
pub struct FileAccount {
2020-02-26 10:54:10 +02:00
root_mailbox : String ,
2018-08-19 13:12:48 +03:00
format : String ,
2018-08-30 15:54:30 +03:00
identity : String ,
2019-04-06 00:30:06 +03:00
#[ serde(default = " none " ) ]
2018-08-30 15:54:30 +03:00
display_name : Option < String > ,
2019-04-06 00:30:06 +03:00
2019-07-18 20:16:51 +03:00
#[ serde(default = " false_val " ) ]
read_only : bool ,
2020-02-08 23:45:49 +02:00
#[ serde(default) ]
2020-02-26 10:54:10 +02:00
subscribed_mailboxes : Vec < String > ,
2019-11-15 19:51:42 +02:00
#[ serde(default) ]
2020-02-26 10:54:10 +02:00
mailboxes : HashMap < String , FileMailboxConf > ,
2019-11-07 22:35:30 +02:00
#[ serde(default) ]
cache_type : CacheType ,
2020-05-29 20:25:11 +03:00
#[ serde(default = " false_val " ) ]
2019-12-14 18:55:46 +02:00
pub manual_refresh : bool ,
2020-01-08 21:41:57 +02:00
#[ serde(default = " none " ) ]
pub refresh_command : Option < String > ,
2019-12-14 18:55:46 +02:00
#[ serde(flatten) ]
2020-03-18 19:13:07 +02:00
pub conf_override : MailUIConf ,
#[ serde(flatten) ]
2019-12-14 18:55:46 +02:00
#[ serde(deserialize_with = " extra_settings " ) ]
pub extra : HashMap < String , String > , /* use custom deserializer to convert any given value (eg bool, number, etc) to string */
2018-08-19 13:12:48 +03:00
}
2018-08-30 15:54:30 +03:00
impl From < FileAccount > for AccountConf {
fn from ( x : FileAccount ) -> Self {
let format = x . format . to_lowercase ( ) ;
2020-02-26 10:54:10 +02:00
let root_mailbox = x . root_mailbox . clone ( ) ;
2018-08-30 15:54:30 +03:00
let identity = x . identity . clone ( ) ;
let display_name = x . display_name . clone ( ) ;
2020-02-26 10:54:10 +02:00
let mailboxes = x
. mailboxes
2019-11-15 19:51:42 +02:00
. iter ( )
2020-02-26 10:54:10 +02:00
. map ( | ( k , v ) | ( k . clone ( ) , v . mailbox_conf . clone ( ) ) )
2019-11-15 19:51:42 +02:00
. collect ( ) ;
2018-08-30 15:54:30 +03:00
2020-02-08 23:45:49 +02:00
let acc = AccountSettings {
2018-08-30 15:54:30 +03:00
name : String ::new ( ) ,
2020-02-26 10:54:10 +02:00
root_mailbox ,
2018-08-30 15:54:30 +03:00
format ,
identity ,
2019-07-18 20:16:51 +03:00
read_only : x . read_only ,
2018-08-30 15:54:30 +03:00
display_name ,
2020-02-26 10:54:10 +02:00
subscribed_mailboxes : x . subscribed_mailboxes . clone ( ) ,
mailboxes ,
2019-12-14 18:55:46 +02:00
manual_refresh : x . manual_refresh ,
2019-06-28 19:34:40 +03:00
extra : x . extra . clone ( ) ,
2018-08-30 15:54:30 +03:00
} ;
2020-02-26 10:54:10 +02:00
let mailbox_confs = x . mailboxes . clone ( ) ;
2018-08-30 15:54:30 +03:00
AccountConf {
account : acc ,
2020-03-18 19:13:07 +02:00
conf_override : x . conf_override . clone ( ) ,
2018-08-30 15:54:30 +03:00
conf : x ,
2020-02-26 10:54:10 +02:00
mailbox_confs ,
2018-08-30 15:54:30 +03:00
}
}
}
2018-08-19 13:12:48 +03:00
impl FileAccount {
2020-02-26 10:54:10 +02:00
pub fn mailboxes ( & self ) -> & HashMap < String , FileMailboxConf > {
& self . mailboxes
2018-08-19 14:54:32 +03:00
}
2019-09-16 14:09:08 +03:00
2020-02-26 10:54:10 +02:00
pub fn mailbox ( & self ) -> & str {
& self . root_mailbox
2018-08-19 13:12:48 +03:00
}
2019-09-16 14:09:08 +03:00
2019-11-07 22:35:30 +02:00
pub fn cache_type ( & self ) -> & CacheType {
& self . cache_type
}
2018-08-19 13:12:48 +03:00
}
2019-08-25 21:44:19 +03:00
#[ derive(Debug, Clone, Default, Serialize, Deserialize) ]
2019-11-22 18:43:24 +02:00
pub struct FileSettings {
2020-01-24 16:15:31 +02:00
pub accounts : HashMap < String , FileAccount > ,
2019-06-10 18:29:49 +03:00
#[ serde(default) ]
2020-01-24 16:15:31 +02:00
pub pager : PagerSettings ,
2019-06-10 18:29:49 +03:00
#[ serde(default) ]
2020-01-24 16:15:31 +02:00
pub listing : ListingSettings ,
2020-01-08 21:41:57 +02:00
#[ serde(default) ]
2020-01-24 16:15:31 +02:00
pub notifications : NotificationsSettings ,
2019-06-10 18:29:49 +03:00
#[ serde(default) ]
2020-01-24 16:15:31 +02:00
pub shortcuts : Shortcuts ,
pub composing : ComposingSettings ,
2019-09-26 12:10:36 +03:00
#[ serde(default) ]
2020-01-24 16:15:31 +02:00
pub tags : TagsSettings ,
2019-12-01 12:05:14 +02:00
#[ serde(default) ]
2020-01-24 16:15:31 +02:00
pub pgp : PGPSettings ,
2019-10-06 10:58:47 +03:00
#[ serde(default) ]
2020-01-24 16:15:31 +02:00
pub terminal : TerminalSettings ,
2019-12-23 17:08:57 +02:00
#[ serde(default) ]
2020-01-24 16:15:31 +02:00
pub plugins : HashMap < String , Plugin > ,
2020-05-28 16:02:57 +03:00
#[ serde(default) ]
pub log : LogSettings ,
2018-08-19 13:12:48 +03:00
}
2018-08-19 13:30:43 +03:00
#[ derive(Debug, Clone, Default) ]
2018-08-19 14:08:20 +03:00
pub struct AccountConf {
2019-09-09 21:38:37 +03:00
pub ( crate ) account : AccountSettings ,
pub ( crate ) conf : FileAccount ,
2020-03-18 19:13:07 +02:00
pub conf_override : MailUIConf ,
2020-02-26 10:54:10 +02:00
pub ( crate ) mailbox_confs : HashMap < String , FileMailboxConf > ,
2018-08-19 13:30:43 +03:00
}
2018-08-19 14:08:20 +03:00
impl AccountConf {
2018-08-19 13:30:43 +03:00
pub fn account ( & self ) -> & AccountSettings {
& self . account
}
2020-03-18 19:13:07 +02:00
pub fn account_mut ( & mut self ) -> & mut AccountSettings {
& mut self . account
}
2018-08-19 13:30:43 +03:00
pub fn conf ( & self ) -> & FileAccount {
& self . conf
}
2018-08-19 14:08:20 +03:00
pub fn conf_mut ( & mut self ) -> & mut FileAccount {
& mut self . conf
}
2018-08-19 13:30:43 +03:00
}
2018-08-19 13:12:48 +03:00
#[ derive(Debug, Clone, Default) ]
pub struct Settings {
2018-08-19 14:08:20 +03:00
pub accounts : HashMap < String , AccountConf > ,
2018-08-19 13:12:48 +03:00
pub pager : PagerSettings ,
2020-01-08 21:41:57 +02:00
pub listing : ListingSettings ,
2019-03-02 21:40:57 +02:00
pub notifications : NotificationsSettings ,
2019-03-04 10:15:11 +02:00
pub shortcuts : Shortcuts ,
2019-12-01 12:05:14 +02:00
pub tags : TagsSettings ,
2019-09-27 12:48:48 +03:00
pub composing : ComposingSettings ,
2019-09-26 12:10:36 +03:00
pub pgp : PGPSettings ,
2019-10-06 10:58:47 +03:00
pub terminal : TerminalSettings ,
2019-12-23 17:08:57 +02:00
pub plugins : HashMap < String , Plugin > ,
2020-05-28 16:02:57 +03:00
pub log : LogSettings ,
2018-08-19 13:12:48 +03:00
}
2020-06-07 18:02:20 +03:00
pub fn get_config_file ( ) -> Result < PathBuf > {
let xdg_dirs = xdg ::BaseDirectories ::with_prefix ( " meli " ) . map_err ( | err | {
MeliError ::new ( format! (
" Could not detect XDG directories for user: {} " ,
err
) )
. set_source ( Some ( std ::sync ::Arc ::new ( Box ::new ( err ) ) ) )
} ) ? ;
match env ::var ( " MELI_CONFIG " ) {
Ok ( path ) = > Ok ( PathBuf ::from ( path ) ) ,
Err ( _ ) = > Ok ( xdg_dirs
. place_config_file ( " config.toml " )
. chain_err_summary ( | | {
format! (
" Cannot create configuration directory in {} " ,
xdg_dirs . get_config_home ( ) . display ( )
)
} ) ? ) ,
}
}
2018-08-19 13:12:48 +03:00
impl FileSettings {
2018-08-30 15:54:30 +03:00
pub fn new ( ) -> Result < FileSettings > {
2020-06-07 18:02:20 +03:00
let config_path = get_config_file ( ) ? ;
2018-09-05 08:22:10 +03:00
if ! config_path . exists ( ) {
2019-06-10 18:29:49 +03:00
println! (
" No configuration found. Would you like to generate one in {}? [Y/n] " ,
2018-09-05 08:22:10 +03:00
config_path . display ( )
) ;
2019-06-10 18:29:49 +03:00
let mut buffer = String ::new ( ) ;
let stdin = io ::stdin ( ) ;
let mut handle = stdin . lock ( ) ;
loop {
buffer . clear ( ) ;
handle
. read_line ( & mut buffer )
. expect ( " Could not read from stdin. " ) ;
match buffer . trim ( ) {
" Y " | " y " | " yes " | " YES " | " Yes " = > {
2019-11-24 17:00:55 +02:00
create_config_file ( & config_path ) ? ;
2019-11-16 00:33:22 +02:00
return Err ( MeliError ::new (
" Edit the sample configuration and relaunch meli. " ,
) ) ;
2019-06-10 18:29:49 +03:00
}
" n " | " N " | " no " | " No " | " NO " = > {
2019-11-16 00:33:22 +02:00
return Err ( MeliError ::new ( " No configuration file found. " ) ) ;
2019-06-10 18:29:49 +03:00
}
_ = > {
println! (
" No configuration found. Would you like to generate one in {}? [Y/n] " ,
config_path . display ( )
) ;
}
}
}
2018-09-05 08:22:10 +03:00
}
2019-08-25 14:14:54 +03:00
2020-06-07 18:02:20 +03:00
FileSettings ::validate ( config_path )
2018-08-19 13:12:48 +03:00
}
2019-11-22 18:43:24 +02:00
2020-06-07 18:02:20 +03:00
pub fn validate ( path : PathBuf ) -> Result < Self > {
let s = pp ::pp ( & path ) ? ;
2020-01-24 16:05:25 +02:00
let mut s : FileSettings = toml ::from_str ( & s ) . map_err ( | e | {
2019-12-01 22:29:31 +02:00
MeliError ::new ( format! (
" {}: \n Config file contains errors: {} " ,
2020-06-07 18:02:20 +03:00
path . display ( ) ,
2019-12-01 22:29:31 +02:00
e . to_string ( )
) )
2019-11-27 14:22:53 +02:00
} ) ? ;
2019-12-27 15:20:02 +02:00
let mut backends = melib ::backends ::Backends ::new ( ) ;
let plugin_manager = crate ::plugins ::PluginManager ::new ( ) ;
for ( _ , p ) in s . plugins . clone ( ) {
if crate ::plugins ::PluginKind ::Backend = = p . kind ( ) {
crate ::plugins ::backend ::PluginBackend ::register (
plugin_manager . listener ( ) ,
p . clone ( ) ,
& mut backends ,
) ;
}
}
2020-01-24 16:05:25 +02:00
2020-06-02 02:47:02 +03:00
let Themes {
2020-01-24 16:15:31 +02:00
light : default_light ,
dark : default_dark ,
..
2020-06-02 02:47:02 +03:00
} = Themes ::default ( ) ;
2020-06-02 15:40:05 +03:00
for ( k , v ) in default_light . keys . into_iter ( ) {
2020-01-24 16:15:31 +02:00
if ! s . terminal . themes . light . contains_key ( & k ) {
s . terminal . themes . light . insert ( k , v ) ;
}
}
for theme in s . terminal . themes . other_themes . values_mut ( ) {
2020-06-02 15:40:05 +03:00
for ( k , v ) in default_dark . keys . clone ( ) . into_iter ( ) {
2020-01-24 16:15:31 +02:00
if ! theme . contains_key ( & k ) {
theme . insert ( k , v ) ;
2020-01-24 16:05:25 +02:00
}
}
}
2020-06-02 15:40:05 +03:00
for ( k , v ) in default_dark . keys . into_iter ( ) {
2020-01-24 16:15:31 +02:00
if ! s . terminal . themes . dark . contains_key ( & k ) {
s . terminal . themes . dark . insert ( k , v ) ;
}
}
2020-01-24 16:05:25 +02:00
match s . terminal . theme . as_str ( ) {
" dark " | " light " = > { }
t if s . terminal . themes . other_themes . contains_key ( t ) = > { }
t = > {
return Err ( MeliError ::new ( format! ( " Theme ` {} ` was not found. " , t ) ) ) ;
}
}
2020-01-24 16:15:31 +02:00
s . terminal . themes . validate ( ) ? ;
for ( name , acc ) in & s . accounts {
2019-11-27 14:22:53 +02:00
let FileAccount {
2020-02-26 10:54:10 +02:00
root_mailbox ,
2019-11-27 14:22:53 +02:00
format ,
identity ,
read_only ,
display_name ,
2020-02-26 10:54:10 +02:00
subscribed_mailboxes ,
mailboxes ,
2019-11-27 14:22:53 +02:00
extra ,
2019-12-14 18:55:46 +02:00
manual_refresh ,
2020-01-08 21:41:57 +02:00
refresh_command : _ ,
2019-11-27 14:22:53 +02:00
cache_type : _ ,
2020-03-18 19:13:07 +02:00
conf_override : _ ,
2020-01-24 16:15:31 +02:00
} = acc . clone ( ) ;
2019-11-27 14:22:53 +02:00
let lowercase_format = format . to_lowercase ( ) ;
let s = AccountSettings {
2020-01-24 16:15:31 +02:00
name : name . to_string ( ) ,
2020-02-26 10:54:10 +02:00
root_mailbox ,
2019-11-27 14:22:53 +02:00
format : format . clone ( ) ,
identity ,
read_only ,
display_name ,
2020-02-26 10:54:10 +02:00
subscribed_mailboxes ,
2019-12-14 18:55:46 +02:00
manual_refresh ,
2020-02-26 10:54:10 +02:00
mailboxes : mailboxes
2019-11-27 14:22:53 +02:00
. into_iter ( )
2020-02-26 10:54:10 +02:00
. map ( | ( k , v ) | ( k , v . mailbox_conf ) )
2019-11-27 14:22:53 +02:00
. collect ( ) ,
extra ,
} ;
backends . validate_config ( & lowercase_format , & s ) ? ;
2019-11-22 18:43:24 +02:00
}
2020-01-24 16:15:31 +02:00
Ok ( s )
2019-11-22 18:43:24 +02:00
}
2018-08-19 13:12:48 +03:00
}
impl Settings {
2019-11-16 00:33:22 +02:00
pub fn new ( ) -> Result < Settings > {
let fs = FileSettings ::new ( ) ? ;
2018-08-19 14:08:20 +03:00
let mut s : HashMap < String , AccountConf > = HashMap ::new ( ) ;
2018-08-19 13:12:48 +03:00
for ( id , x ) in fs . accounts {
2018-08-30 15:54:30 +03:00
let mut ac = AccountConf ::from ( x ) ;
ac . account . set_name ( id . clone ( ) ) ;
s . insert ( id , ac ) ;
2018-08-19 13:12:48 +03:00
}
2020-05-28 16:02:57 +03:00
if let Some ( ref log_path ) = fs . log . log_file {
melib ::change_log_dest ( log_path . into ( ) ) ;
}
if fs . log . maximum_level ! = melib ::LoggingLevel ::default ( ) {
melib ::change_log_level ( fs . log . maximum_level ) ;
}
2019-11-16 00:33:22 +02:00
Ok ( Settings {
2018-08-19 13:12:48 +03:00
accounts : s ,
pager : fs . pager ,
2020-01-08 21:41:57 +02:00
listing : fs . listing ,
2019-03-02 21:40:57 +02:00
notifications : fs . notifications ,
2019-03-03 14:24:15 +02:00
shortcuts : fs . shortcuts ,
2019-12-01 12:05:14 +02:00
tags : fs . tags ,
2019-09-27 12:48:48 +03:00
composing : fs . composing ,
2019-09-26 12:10:36 +03:00
pgp : fs . pgp ,
2019-10-06 10:58:47 +03:00
terminal : fs . terminal ,
2019-12-23 17:08:57 +02:00
plugins : fs . plugins ,
2020-05-28 16:02:57 +03:00
log : fs . log ,
2019-11-16 00:33:22 +02:00
} )
2018-08-19 13:12:48 +03:00
}
}
2019-02-18 23:14:06 +02:00
2019-09-09 21:38:37 +03:00
#[ derive(Copy, Debug, Clone, Hash, PartialEq) ]
2019-02-18 23:14:06 +02:00
pub enum IndexStyle {
Plain ,
Threaded ,
Compact ,
2019-09-14 12:43:19 +03:00
Conversations ,
2019-02-18 23:14:06 +02:00
}
impl Default for IndexStyle {
fn default ( ) -> Self {
IndexStyle ::Compact
}
}
2019-04-06 00:30:06 +03:00
/*
* Deserialize default functions
* /
mod default_vals {
2020-03-18 19:13:07 +02:00
pub ( in crate ::conf ) fn false_val < T : std ::convert ::From < bool > > ( ) -> T {
false . into ( )
2019-04-06 00:30:06 +03:00
}
2020-03-18 19:13:07 +02:00
pub ( in crate ::conf ) fn true_val < T : std ::convert ::From < bool > > ( ) -> T {
true . into ( )
2019-04-06 00:30:06 +03:00
}
2020-03-18 19:13:07 +02:00
pub ( in crate ::conf ) fn zero_val < T : std ::convert ::From < usize > > ( ) -> T {
0. into ( )
2019-04-06 00:30:06 +03:00
}
2020-03-18 19:13:07 +02:00
pub ( in crate ::conf ) fn eighty_val < T : std ::convert ::From < usize > > ( ) -> T {
80. into ( )
2019-04-06 00:30:06 +03:00
}
2019-08-23 21:32:32 +03:00
pub ( in crate ::conf ) fn none < T > ( ) -> Option < T > {
2019-04-06 00:30:06 +03:00
None
}
2019-09-15 23:36:30 +03:00
2020-03-18 19:13:07 +02:00
pub ( in crate ::conf ) fn internal_value_false < T : std ::convert ::From < super ::ToggleFlag > > ( ) -> T {
super ::ToggleFlag ::InternalVal ( false ) . into ( )
2019-09-15 23:36:30 +03:00
}
2020-01-02 00:07:19 +02:00
2020-03-18 19:13:07 +02:00
pub ( in crate ::conf ) fn internal_value_true < T : std ::convert ::From < super ::ToggleFlag > > ( ) -> T {
super ::ToggleFlag ::InternalVal ( true ) . into ( )
2020-01-02 00:07:19 +02:00
}
2019-04-06 00:30:06 +03:00
}
2019-09-09 21:38:37 +03:00
2019-10-03 19:51:34 +03:00
mod deserializers {
use serde ::{ Deserialize , Deserializer } ;
2020-03-18 19:13:07 +02:00
pub ( in crate ::conf ) fn non_empty_string < ' de , D , T : std ::convert ::From < Option < String > > > (
2019-10-03 19:51:34 +03:00
deserializer : D ,
2020-03-18 19:13:07 +02:00
) -> std ::result ::Result < T , D ::Error >
2019-10-03 19:51:34 +03:00
where
D : Deserializer < ' de > ,
{
let s = < String > ::deserialize ( deserializer ) ? ;
if s . is_empty ( ) {
2020-03-18 19:13:07 +02:00
Ok ( None . into ( ) )
2019-10-03 19:51:34 +03:00
} else {
2020-03-18 19:13:07 +02:00
Ok ( Some ( s ) . into ( ) )
2019-10-03 19:51:34 +03:00
}
}
2019-10-24 18:16:41 +03:00
use toml ::Value ;
fn any_of < ' de , D > ( deserializer : D ) -> std ::result ::Result < String , D ::Error >
where
D : Deserializer < ' de > ,
{
let v : Value = Deserialize ::deserialize ( deserializer ) ? ;
let mut ret = v . to_string ( ) ;
if ret . starts_with ( '"' ) & & ret . ends_with ( '"' ) {
ret . drain ( 0 .. 1 ) . count ( ) ;
ret . drain ( ret . len ( ) - 1 .. ) . count ( ) ;
}
Ok ( ret )
}
use std ::collections ::HashMap ;
pub ( in crate ::conf ) fn extra_settings < ' de , D > (
deserializer : D ,
) -> std ::result ::Result < HashMap < String , String > , D ::Error >
where
D : Deserializer < ' de > ,
{
/* Why is this needed? If the user gives a configuration value such as key = true, the
* parsing will fail since it expects string values . We want to accept key = true as well
* as key = " true " . * /
#[ derive(Deserialize) ]
struct Wrapper ( #[ serde(deserialize_with = " any_of " ) ] String ) ;
let v = < HashMap < String , Wrapper > > ::deserialize ( deserializer ) ? ;
Ok ( v . into_iter ( ) . map ( | ( k , Wrapper ( v ) ) | ( k , v ) ) . collect ( ) )
}
2019-10-03 19:51:34 +03:00
}
2019-09-09 21:38:37 +03:00
impl < ' de > Deserialize < ' de > for IndexStyle {
fn deserialize < D > ( deserializer : D ) -> std ::result ::Result < Self , D ::Error >
where
D : Deserializer < ' de > ,
{
let s = < String > ::deserialize ( deserializer ) ? ;
match s . as_str ( ) {
" Plain " | " plain " = > Ok ( IndexStyle ::Plain ) ,
" Threaded " | " threaded " = > Ok ( IndexStyle ::Threaded ) ,
" Compact " | " compact " = > Ok ( IndexStyle ::Compact ) ,
2019-09-14 12:43:19 +03:00
" Conversations " | " conversations " = > Ok ( IndexStyle ::Conversations ) ,
_ = > Err ( de ::Error ::custom ( " invalid `index_style` value " ) ) ,
2019-09-09 21:38:37 +03:00
}
}
}
impl Serialize for IndexStyle {
fn serialize < S > ( & self , serializer : S ) -> std ::result ::Result < S ::Ok , S ::Error >
where
S : Serializer ,
{
match self {
IndexStyle ::Plain = > serializer . serialize_str ( " plain " ) ,
IndexStyle ::Threaded = > serializer . serialize_str ( " threaded " ) ,
IndexStyle ::Compact = > serializer . serialize_str ( " compact " ) ,
2019-09-14 12:43:19 +03:00
IndexStyle ::Conversations = > serializer . serialize_str ( " conversations " ) ,
2019-09-09 21:38:37 +03:00
}
}
}
2019-10-30 21:09:48 +02:00
2019-11-07 22:35:30 +02:00
#[ derive(Debug, Clone, PartialEq) ]
pub enum CacheType {
None ,
2019-10-30 21:09:48 +02:00
#[ cfg(feature = " sqlite3 " ) ]
Sqlite3 ,
}
2019-11-07 22:35:30 +02:00
impl Default for CacheType {
2019-10-30 21:09:48 +02:00
fn default ( ) -> Self {
#[ cfg(feature = " sqlite3 " ) ]
{
2019-11-07 22:35:30 +02:00
CacheType ::Sqlite3
2019-10-30 21:09:48 +02:00
}
#[ cfg(not(feature = " sqlite3 " )) ]
{
2019-11-07 22:35:30 +02:00
CacheType ::None
}
}
}
impl < ' de > Deserialize < ' de > for CacheType {
fn deserialize < D > ( deserializer : D ) -> std ::result ::Result < Self , D ::Error >
where
D : Deserializer < ' de > ,
{
let s = < String > ::deserialize ( deserializer ) ? ;
match s . as_str ( ) {
#[ cfg(feature = " sqlite3 " ) ]
" sqlite3 " = > Ok ( CacheType ::Sqlite3 ) ,
" nothing " | " none " | " " = > Ok ( CacheType ::None ) ,
_ = > Err ( de ::Error ::custom ( " invalid `index_cache` value " ) ) ,
}
}
}
impl Serialize for CacheType {
fn serialize < S > ( & self , serializer : S ) -> std ::result ::Result < S ::Ok , S ::Error >
where
S : Serializer ,
{
match self {
#[ cfg(feature = " sqlite3 " ) ]
CacheType ::Sqlite3 = > serializer . serialize_str ( " sqlite3 " ) ,
CacheType ::None = > serializer . serialize_str ( " none " ) ,
2019-10-30 21:09:48 +02:00
}
}
}
2019-11-23 17:56:38 +02:00
2019-11-24 17:00:55 +02:00
pub fn create_config_file ( p : & Path ) -> Result < ( ) > {
let mut file = OpenOptions ::new ( )
. write ( true )
. create_new ( true )
. open ( p )
. expect ( " Could not create config file. " ) ;
2020-06-02 18:26:44 +03:00
file . write_all ( include_bytes! ( " ../samples/sample-config.toml " ) )
2019-11-24 17:00:55 +02:00
. expect ( " Could not write to config file. " ) ;
println! ( " Written example configuration to {} " , p . display ( ) ) ;
let metadata = file . metadata ( ) ? ;
let mut permissions = metadata . permissions ( ) ;
permissions . set_mode ( 0o600 ) ; // Read/write for owner only.
file . set_permissions ( permissions ) ? ;
Ok ( ( ) )
}
2019-11-27 22:13:14 +02:00
mod pp {
2020-02-04 15:52:12 +02:00
//! Preprocess configuration files by unfolding `include` macros.
2019-11-27 22:13:14 +02:00
use melib ::{
error ::{ MeliError , Result } ,
parsec ::* ,
2020-02-08 23:51:33 +02:00
ShellExpandTrait ,
2019-11-27 22:13:14 +02:00
} ;
use std ::io ::Read ;
2019-11-29 12:15:05 +02:00
use std ::path ::{ Path , PathBuf } ;
2019-11-27 22:13:14 +02:00
2020-02-04 15:52:12 +02:00
/// Try to parse line into a path to be included.
2019-11-27 22:13:14 +02:00
fn include_directive < ' a > ( ) -> impl Parser < ' a , Option < & ' a str > > {
move | input : & ' a str | {
enum State {
Start ,
Path ,
}
use State ::* ;
let mut state = State ::Start ;
let mut i = 0 ;
while i < input . len ( ) {
match ( & state , input . as_bytes ( ) [ i ] ) {
( Start , b '#' ) = > {
return Ok ( ( " " , None ) ) ;
}
2020-01-29 05:54:13 +02:00
( Start , b ) if ( b as char ) . is_whitespace ( ) = > { /* consume */ }
( Start , _ ) if input . as_bytes ( ) [ i .. ] . starts_with ( b " include( " ) = > {
i + = " include( " . len ( ) ;
2019-11-27 22:13:14 +02:00
state = Path ;
continue ;
}
2020-01-29 05:54:13 +02:00
( Start , _ ) = > {
2019-11-27 22:13:14 +02:00
return Ok ( ( " " , None ) ) ;
}
2020-01-29 05:54:13 +02:00
( Path , b '"' ) | ( Path , b '\'' ) | ( Path , b '`' ) = > {
2019-11-27 22:13:14 +02:00
let mut end = i + 1 ;
while end < input . len ( ) & & input . as_bytes ( ) [ end ] ! = input . as_bytes ( ) [ i ] {
end + = 1 ;
}
if end = = input . len ( ) {
return Err ( input ) ;
}
let ret = & input [ i + 1 .. end ] ;
end + = 1 ;
2020-01-29 05:54:13 +02:00
if end < input . len ( ) & & input . as_bytes ( ) [ end ] ! = b ')' {
/* Nothing else allowed in line */
return Err ( input ) ;
}
end + = 1 ;
2019-11-27 22:13:14 +02:00
while end < input . len ( ) {
if ! ( input . as_bytes ( ) [ end ] as char ) . is_whitespace ( ) {
/* Nothing else allowed in line */
return Err ( input ) ;
}
end + = 1 ;
}
return Ok ( ( " " , Some ( ret ) ) ) ;
}
( Path , _ ) = > return Err ( input ) ,
}
i + = 1 ;
}
return Ok ( ( " " , None ) ) ;
}
}
2020-02-04 15:52:12 +02:00
/// Expands `include` macros in path.
2019-11-29 12:15:05 +02:00
fn pp_helper ( path : & Path , level : u8 ) -> Result < String > {
2019-11-27 22:13:14 +02:00
if level > 7 {
2019-11-29 12:15:05 +02:00
return Err ( MeliError ::new ( format! ( " Maximum recursion limit reached while unfolding include directives in {} . Have you included a config file within itself? " , path . display ( ) ) ) ) ;
2019-11-27 22:13:14 +02:00
}
let mut contents = String ::new ( ) ;
let mut file = std ::fs ::File ::open ( path ) ? ;
file . read_to_string ( & mut contents ) ? ;
2020-01-29 05:54:13 +02:00
let mut ret = String ::with_capacity ( contents . len ( ) ) ;
2019-11-27 22:13:14 +02:00
for ( i , l ) in contents . lines ( ) . enumerate ( ) {
2020-01-29 05:54:13 +02:00
if let ( _ , Some ( sub_path ) ) = include_directive ( ) . parse ( l ) . map_err ( | l | {
2019-11-27 22:13:14 +02:00
MeliError ::new ( format! (
2020-01-29 05:54:13 +02:00
" Malformed include directive in line {} of file {}: {} \n Configuration uses the standard m4 macro include(`filename`). " ,
2019-11-29 12:15:05 +02:00
i ,
path . display ( ) ,
l
2019-11-27 22:13:14 +02:00
) )
} ) ? {
2020-02-08 23:51:33 +02:00
let mut p = Path ::new ( sub_path ) . expand ( ) ;
if p . is_relative ( ) {
2019-11-29 12:15:05 +02:00
/* We checked that path is ok above so we can do unwrap here */
debug! ( path ) ;
let prefix = path . parent ( ) . unwrap ( ) ;
debug! ( prefix ) ;
2020-02-08 23:51:33 +02:00
p = prefix . join ( p )
}
2019-11-29 12:15:05 +02:00
2020-02-08 23:51:33 +02:00
ret . extend ( pp_helper ( & p , level + 1 ) ? . chars ( ) ) ;
2020-01-29 05:54:13 +02:00
} else {
ret . push_str ( l ) ;
ret . push ( '\n' ) ;
2019-11-27 22:13:14 +02:00
}
}
2020-01-29 05:54:13 +02:00
Ok ( ret )
2019-11-27 22:13:14 +02:00
}
2019-11-29 12:15:05 +02:00
2020-02-04 15:52:12 +02:00
/// Expands `include` macros in configuration file and other configuration files (eg. themes)
/// in the filesystem.
2020-01-24 16:05:25 +02:00
pub fn pp < P : AsRef < Path > > ( path : P ) -> Result < String > {
let p_buf : PathBuf = if path . as_ref ( ) . is_relative ( ) {
2020-02-08 23:51:33 +02:00
path . as_ref ( ) . expand ( ) . canonicalize ( ) ?
2019-11-29 12:15:05 +02:00
} else {
2020-02-08 23:51:33 +02:00
path . as_ref ( ) . expand ( )
2019-11-29 12:15:05 +02:00
} ;
2020-01-24 16:15:31 +02:00
let mut ret = pp_helper ( & p_buf , 0 ) ? ;
2019-11-29 12:15:05 +02:00
drop ( p_buf ) ;
2020-01-24 16:15:31 +02:00
if let Ok ( xdg_dirs ) = xdg ::BaseDirectories ::with_prefix ( " meli " ) {
2020-02-26 10:54:10 +02:00
for theme_mailbox in xdg_dirs . find_config_files ( " themes " ) {
let read_dir = std ::fs ::read_dir ( theme_mailbox ) ? ;
2020-01-24 16:15:31 +02:00
for theme in read_dir {
ret . extend ( pp_helper ( & theme ? . path ( ) , 0 ) ? . chars ( ) ) ;
}
}
}
Ok ( ret )
2019-11-27 22:13:14 +02:00
}
}
2020-05-28 16:02:57 +03:00
#[ derive(Debug, Clone, Default, Serialize, Deserialize) ]
pub struct LogSettings {
#[ serde(default) ]
log_file : Option < PathBuf > ,
#[ serde(default) ]
maximum_level : melib ::LoggingLevel ,
}