2019-09-15 16:29:11 +00:00
/*
* This file is part of espanso .
*
* Copyright ( C ) 2019 Federico Terzi
*
* espanso 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 .
*
* espanso 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 espanso . If not , see < https ://www.gnu.org/licenses/>.
* /
2019-09-09 15:59:44 +00:00
extern crate dirs ;
2019-09-12 20:14:41 +00:00
use crate ::event ::KeyModifier ;
2019-11-29 21:09:02 +00:00
use crate ::keyboard ::PasteShortcut ;
2020-05-10 16:01:04 +00:00
use crate ::matcher ::{ Match , MatchVariable } ;
use log ::error ;
use serde ::{ Deserialize , Serialize } ;
use std ::collections ::{ HashMap , HashSet } ;
2019-09-10 20:53:45 +00:00
use std ::error ::Error ;
2020-05-10 16:01:04 +00:00
use std ::fmt ;
use std ::fs ;
use std ::fs ::{ create_dir_all , File } ;
use std ::io ::Read ;
use std ::path ::{ Path , PathBuf } ;
2020-05-26 19:18:27 +00:00
use walkdir ::{ DirEntry , WalkDir } ;
2019-09-09 15:59:44 +00:00
pub ( crate ) mod runtime ;
2020-05-10 16:01:04 +00:00
const DEFAULT_CONFIG_FILE_CONTENT : & str = include_str! ( " ../res/config.yml " ) ;
2019-09-09 15:59:44 +00:00
2020-05-10 16:01:04 +00:00
pub const DEFAULT_CONFIG_FILE_NAME : & str = " default.yml " ;
2020-02-27 20:56:20 +00:00
pub const USER_CONFIGS_FOLDER_NAME : & str = " user " ;
2019-09-09 15:59:44 +00:00
// Default values for primitives
2020-05-10 16:01:04 +00:00
fn default_name ( ) -> String {
" default " . to_owned ( )
}
fn default_parent ( ) -> String {
" self " . to_owned ( )
}
fn default_filter_title ( ) -> String {
" " . to_owned ( )
}
fn default_filter_class ( ) -> String {
" " . to_owned ( )
}
fn default_filter_exec ( ) -> String {
" " . to_owned ( )
}
fn default_log_level ( ) -> i32 {
0
}
fn default_conflict_check ( ) -> bool {
false
}
fn default_ipc_server_port ( ) -> i32 {
34982
}
fn default_worker_ipc_server_port ( ) -> i32 {
34983
}
fn default_use_system_agent ( ) -> bool {
true
}
fn default_config_caching_interval ( ) -> i32 {
800
}
fn default_word_separators ( ) -> Vec < char > {
vec! [ ' ' , ',' , '.' , '?' , '!' , '\r' , '\n' , 22 u8 as char ]
}
fn default_toggle_interval ( ) -> u32 {
230
}
fn default_toggle_key ( ) -> KeyModifier {
KeyModifier ::ALT
}
fn default_preserve_clipboard ( ) -> bool {
true
}
fn default_passive_match_regex ( ) -> String {
" (?P<name>: \\ p{L}+)(/(?P<args>.*)/)? " . to_owned ( )
}
fn default_passive_arg_delimiter ( ) -> char {
'/'
}
fn default_passive_arg_escape ( ) -> char {
'\\'
}
2020-07-15 18:18:25 +00:00
fn default_passive_delay ( ) -> u64 {
100
}
2020-05-10 16:01:04 +00:00
fn default_passive_key ( ) -> KeyModifier {
KeyModifier ::OFF
}
fn default_enable_passive ( ) -> bool {
false
}
fn default_enable_active ( ) -> bool {
true
}
fn default_backspace_limit ( ) -> i32 {
3
}
fn default_backspace_delay ( ) -> i32 {
0
}
fn default_inject_delay ( ) -> i32 {
0
}
fn default_restore_clipboard_delay ( ) -> i32 {
300
}
fn default_exclude_default_entries ( ) -> bool {
false
}
fn default_secure_input_watcher_enabled ( ) -> bool {
true
}
fn default_secure_input_notification ( ) -> bool {
true
}
fn default_show_notifications ( ) -> bool {
true
}
fn default_auto_restart ( ) -> bool {
2020-06-24 20:11:01 +00:00
true
2020-05-10 16:01:04 +00:00
}
2020-08-08 20:25:13 +00:00
fn default_undo_backspace ( ) -> bool {
true
}
2020-05-10 16:01:04 +00:00
fn default_show_icon ( ) -> bool {
true
}
fn default_fast_inject ( ) -> bool {
true
}
fn default_secure_input_watcher_interval ( ) -> i32 {
5000
}
fn default_matches ( ) -> Vec < Match > {
Vec ::new ( )
}
fn default_global_vars ( ) -> Vec < MatchVariable > {
Vec ::new ( )
}
2020-08-03 20:03:39 +00:00
fn default_modulo_path ( ) -> Option < String > {
None
}
2020-08-12 18:37:15 +00:00
fn default_mac_post_inject_delay ( ) -> u64 {
100
}
2019-09-09 15:59:44 +00:00
#[ derive(Clone, Debug, Serialize, Deserialize) ]
pub struct Configs {
2020-02-27 20:56:20 +00:00
#[ serde(default = " default_name " ) ]
2019-09-09 15:59:44 +00:00
pub name : String ,
2019-09-24 20:53:12 +00:00
#[ serde(default = " default_parent " ) ]
pub parent : String ,
2019-09-09 15:59:44 +00:00
#[ serde(default = " default_filter_title " ) ]
pub filter_title : String ,
#[ serde(default = " default_filter_class " ) ]
pub filter_class : String ,
#[ serde(default = " default_filter_exec " ) ]
pub filter_exec : String ,
2019-09-14 08:30:51 +00:00
#[ serde(default = " default_log_level " ) ]
pub log_level : i32 ,
2020-01-21 20:58:10 +00:00
#[ serde(default = " default_conflict_check " ) ]
pub conflict_check : bool ,
2019-09-16 08:16:37 +00:00
#[ serde(default = " default_ipc_server_port " ) ]
pub ipc_server_port : i32 ,
2020-05-09 22:02:25 +00:00
#[ serde(default = " default_worker_ipc_server_port " ) ]
pub worker_ipc_server_port : i32 ,
2019-09-16 22:11:31 +00:00
#[ serde(default = " default_use_system_agent " ) ]
pub use_system_agent : bool ,
2019-09-09 15:59:44 +00:00
#[ serde(default = " default_config_caching_interval " ) ]
pub config_caching_interval : i32 ,
2019-10-19 21:31:05 +00:00
#[ serde(default = " default_word_separators " ) ]
2020-05-10 16:01:04 +00:00
pub word_separators : Vec < char > , // TODO: add parsing test
2019-10-19 21:31:05 +00:00
2020-01-18 23:30:30 +00:00
#[ serde(default = " default_toggle_key " ) ]
2019-09-09 15:59:44 +00:00
pub toggle_key : KeyModifier ,
#[ serde(default = " default_toggle_interval " ) ]
pub toggle_interval : u32 ,
2019-12-13 22:17:53 +00:00
#[ serde(default = " default_preserve_clipboard " ) ]
pub preserve_clipboard : bool ,
2020-01-10 22:29:21 +00:00
#[ serde(default = " default_passive_match_regex " ) ]
pub passive_match_regex : String ,
2020-01-18 21:55:50 +00:00
#[ serde(default = " default_passive_arg_delimiter " ) ]
pub passive_arg_delimiter : char ,
#[ serde(default = " default_passive_arg_escape " ) ]
pub passive_arg_escape : char ,
2020-01-10 22:29:21 +00:00
2020-01-18 23:30:30 +00:00
#[ serde(default = " default_passive_key " ) ]
pub passive_key : KeyModifier ,
2020-07-15 18:18:25 +00:00
#[ serde(default = " default_passive_delay " ) ]
pub passive_delay : u64 ,
2020-01-26 21:30:54 +00:00
#[ serde(default = " default_enable_passive " ) ]
pub enable_passive : bool ,
#[ serde(default = " default_enable_active " ) ]
pub enable_active : bool ,
2020-08-08 20:25:13 +00:00
#[ serde(default = " default_undo_backspace " ) ]
pub undo_backspace : bool ,
2019-11-29 21:09:02 +00:00
#[ serde(default) ]
pub paste_shortcut : PasteShortcut ,
2019-09-09 15:59:44 +00:00
#[ serde(default = " default_backspace_limit " ) ]
pub backspace_limit : i32 ,
2020-01-26 21:40:32 +00:00
#[ serde(default = " default_restore_clipboard_delay " ) ]
pub restore_clipboard_delay : i32 ,
2020-04-03 16:22:31 +00:00
#[ serde(default = " default_secure_input_watcher_enabled " ) ]
pub secure_input_watcher_enabled : bool ,
#[ serde(default = " default_secure_input_watcher_interval " ) ]
pub secure_input_watcher_interval : i32 ,
2020-08-15 17:35:14 +00:00
2020-08-12 18:37:15 +00:00
#[ serde(default = " default_mac_post_inject_delay " ) ]
pub mac_post_inject_delay : u64 ,
2020-04-03 16:22:31 +00:00
#[ serde(default = " default_secure_input_notification " ) ]
pub secure_input_notification : bool ,
2019-09-09 15:59:44 +00:00
#[ serde(default) ]
pub backend : BackendType ,
2020-01-26 22:56:50 +00:00
#[ serde(default = " default_exclude_default_entries " ) ]
pub exclude_default_entries : bool ,
2019-09-14 20:14:39 +00:00
2020-04-15 16:56:39 +00:00
#[ serde(default = " default_show_notifications " ) ]
pub show_notifications : bool ,
#[ serde(default = " default_show_icon " ) ]
pub show_icon : bool ,
2020-04-18 17:31:24 +00:00
#[ serde(default = " default_fast_inject " ) ]
pub fast_inject : bool ,
2020-05-05 18:17:45 +00:00
#[ serde(default = " default_backspace_delay " ) ]
pub backspace_delay : i32 ,
2020-05-05 19:20:54 +00:00
#[ serde(default = " default_inject_delay " ) ]
pub inject_delay : i32 ,
2020-05-08 17:04:50 +00:00
#[ serde(default = " default_auto_restart " ) ]
pub auto_restart : bool ,
2019-09-09 15:59:44 +00:00
#[ serde(default = " default_matches " ) ]
2020-01-26 22:56:50 +00:00
pub matches : Vec < Match > ,
#[ serde(default = " default_global_vars " ) ]
2020-05-10 16:01:04 +00:00
pub global_vars : Vec < MatchVariable > ,
2020-08-03 20:03:39 +00:00
#[ serde(default = " default_modulo_path " ) ]
2020-08-15 17:35:14 +00:00
pub modulo_path : Option < String > ,
2019-09-09 15:59:44 +00:00
}
2019-09-09 16:56:55 +00:00
// Macro used to validate config fields
#[ macro_export ]
macro_rules ! validate_field {
( $result :expr , $field :expr , $def_value :expr ) = > {
if $field ! = $def_value {
2019-09-10 20:53:45 +00:00
let mut field_name = stringify! ( $field ) ;
if field_name . starts_with ( " self. " ) {
field_name = & field_name [ 5 .. ] ; // Remove the 'self.' prefix
}
2019-09-23 20:08:46 +00:00
error! ( " Validation error, parameter '{}' is reserved and can be only used in the default.yml config file " , field_name ) ;
2019-09-09 16:56:55 +00:00
$result = false ;
}
} ;
}
impl Configs {
/*
* Validate the Config instance .
2019-09-23 21:34:55 +00:00
* It makes sure that user defined config instances do not define
2019-09-09 16:56:55 +00:00
* attributes reserved to the default config .
* /
2019-09-23 21:34:55 +00:00
fn validate_user_defined_config ( & self ) -> bool {
2019-09-09 16:56:55 +00:00
let mut result = true ;
2020-05-10 16:01:04 +00:00
validate_field! (
result ,
self . config_caching_interval ,
default_config_caching_interval ( )
) ;
2019-09-14 08:30:51 +00:00
validate_field! ( result , self . log_level , default_log_level ( ) ) ;
2020-01-21 20:58:10 +00:00
validate_field! ( result , self . conflict_check , default_conflict_check ( ) ) ;
2020-01-18 23:30:30 +00:00
validate_field! ( result , self . toggle_key , default_toggle_key ( ) ) ;
2019-09-09 16:56:55 +00:00
validate_field! ( result , self . toggle_interval , default_toggle_interval ( ) ) ;
validate_field! ( result , self . backspace_limit , default_backspace_limit ( ) ) ;
2019-09-16 22:11:31 +00:00
validate_field! ( result , self . ipc_server_port , default_ipc_server_port ( ) ) ;
validate_field! ( result , self . use_system_agent , default_use_system_agent ( ) ) ;
2020-05-10 16:01:04 +00:00
validate_field! (
result ,
self . preserve_clipboard ,
default_preserve_clipboard ( )
) ;
validate_field! (
result ,
self . passive_match_regex ,
default_passive_match_regex ( )
) ;
validate_field! (
result ,
self . passive_arg_delimiter ,
default_passive_arg_delimiter ( )
) ;
validate_field! (
result ,
self . passive_arg_escape ,
default_passive_arg_escape ( )
) ;
2020-01-18 23:30:30 +00:00
validate_field! ( result , self . passive_key , default_passive_key ( ) ) ;
2020-05-10 16:01:04 +00:00
validate_field! (
result ,
self . restore_clipboard_delay ,
default_restore_clipboard_delay ( )
) ;
validate_field! (
result ,
self . secure_input_watcher_enabled ,
default_secure_input_watcher_enabled ( )
) ;
validate_field! (
result ,
self . secure_input_watcher_interval ,
default_secure_input_watcher_interval ( )
) ;
validate_field! (
result ,
self . secure_input_notification ,
default_secure_input_notification ( )
) ;
validate_field! (
result ,
self . show_notifications ,
default_show_notifications ( )
) ;
2020-04-15 16:56:39 +00:00
validate_field! ( result , self . show_icon , default_show_icon ( ) ) ;
2019-09-09 16:56:55 +00:00
result
}
}
2019-09-09 15:59:44 +00:00
#[ derive(Debug, Clone, PartialEq, Serialize, Deserialize) ]
pub enum BackendType {
Inject ,
2020-03-08 18:06:01 +00:00
Clipboard ,
// On Linux systems there is a long standing issue with text injection (which
// in general is better than Clipboard copy/pasting) that prevents certain
// apps from correctly handling special characters (such as emojis or accented letters)
// when injected. For this reason, espanso initially defaulted on the Clipboard
// backend on Linux, as it was the most reliable (working in 99% of cases),
// even though it was less efficient and with a few inconveniences (for example, the
// previous clipboard content being overwritten).
// The Auto backend tries to take it a step further, by automatically determining
// when an injection is possible (only ascii characters in the replacement), and falling
// back to the Clipboard backend otherwise.
// Should only be used on Linux systems.
2020-05-10 16:01:04 +00:00
Auto ,
2019-09-09 15:59:44 +00:00
}
impl Default for BackendType {
2019-10-10 17:19:26 +00:00
// The default backend varies based on the operating system.
// On Windows and macOS, the Inject backend is working great and should
// be preferred as it doesn't override the clipboard.
// On the other hand, on linux it has many problems due to the bugs
// of the libxdo used. For this reason, Clipboard will be the default
// backend on Linux from version v0.3.0
#[ cfg(not(target_os = " linux " )) ]
2019-09-09 15:59:44 +00:00
fn default ( ) -> Self {
BackendType ::Inject
}
2019-10-10 17:19:26 +00:00
#[ cfg(target_os = " linux " ) ]
fn default ( ) -> Self {
2020-04-18 17:32:09 +00:00
BackendType ::Auto
2019-10-10 17:19:26 +00:00
}
2019-09-09 15:59:44 +00:00
}
impl Configs {
2019-09-10 20:53:45 +00:00
fn load_config ( path : & Path ) -> Result < Configs , ConfigLoadError > {
2019-09-09 15:59:44 +00:00
let file_res = File ::open ( path ) ;
if let Ok ( mut file ) = file_res {
let mut contents = String ::new ( ) ;
2019-09-10 20:53:45 +00:00
let res = file . read_to_string ( & mut contents ) ;
2019-09-28 08:44:25 +00:00
if res . is_err ( ) {
2020-05-10 16:01:04 +00:00
return Err ( ConfigLoadError ::UnableToReadFile ) ;
2019-09-10 20:53:45 +00:00
}
2019-09-09 15:59:44 +00:00
2019-09-09 16:56:55 +00:00
let config_res = serde_yaml ::from_str ( & contents ) ;
match config_res {
2019-09-10 20:53:45 +00:00
Ok ( config ) = > Ok ( config ) ,
2020-05-10 16:01:04 +00:00
Err ( e ) = > Err ( ConfigLoadError ::InvalidYAML ( path . to_owned ( ) , e . to_string ( ) ) ) ,
2019-09-09 16:56:55 +00:00
}
2020-05-10 16:01:04 +00:00
} else {
2020-04-15 16:47:59 +00:00
eprintln! ( " Error: Cannot load file {:?} " , path ) ;
2019-09-10 20:53:45 +00:00
Err ( ConfigLoadError ::FileNotFound )
2019-09-09 15:59:44 +00:00
}
}
2019-09-24 20:53:12 +00:00
2020-05-26 19:18:27 +00:00
fn merge_overwrite ( & mut self , new_config : Configs ) {
2020-01-26 22:56:50 +00:00
// Merge matches
2019-09-24 20:53:12 +00:00
let mut merged_matches = new_config . matches ;
2020-01-26 22:56:50 +00:00
let mut match_trigger_set = HashSet ::new ( ) ;
2019-09-24 20:53:12 +00:00
merged_matches . iter ( ) . for_each ( | m | {
2020-03-02 20:43:26 +00:00
match_trigger_set . extend ( m . triggers . clone ( ) ) ;
2019-09-24 20:53:12 +00:00
} ) ;
2020-05-10 16:01:04 +00:00
let parent_matches : Vec < Match > = self
. matches
. iter ( )
. filter ( | & m | {
! m . triggers
. iter ( )
. any ( | trigger | match_trigger_set . contains ( trigger ) )
} )
. cloned ( )
. collect ( ) ;
2019-09-24 20:53:12 +00:00
merged_matches . extend ( parent_matches ) ;
self . matches = merged_matches ;
2020-01-26 22:56:50 +00:00
// Merge global variables
let mut merged_global_vars = new_config . global_vars ;
let mut vars_name_set = HashSet ::new ( ) ;
merged_global_vars . iter ( ) . for_each ( | m | {
vars_name_set . insert ( m . name . clone ( ) ) ;
} ) ;
2020-05-10 16:01:04 +00:00
let parent_vars : Vec < MatchVariable > = self
. global_vars
. iter ( )
. filter ( | & m | ! vars_name_set . contains ( & m . name ) )
. cloned ( )
. collect ( ) ;
2020-01-26 22:56:50 +00:00
merged_global_vars . extend ( parent_vars ) ;
self . global_vars = merged_global_vars ;
2019-09-24 20:53:12 +00:00
}
2020-05-26 19:18:27 +00:00
fn merge_no_overwrite ( & mut self , default : & Configs ) {
2020-01-26 22:56:50 +00:00
// Merge matches
let mut match_trigger_set = HashSet ::new ( ) ;
2019-09-24 20:53:12 +00:00
self . matches . iter ( ) . for_each ( | m | {
2020-03-02 20:43:26 +00:00
match_trigger_set . extend ( m . triggers . clone ( ) ) ;
2019-09-24 20:53:12 +00:00
} ) ;
2020-05-10 16:01:04 +00:00
let default_matches : Vec < Match > = default
. matches
. iter ( )
. filter ( | & m | {
! m . triggers
. iter ( )
. any ( | trigger | match_trigger_set . contains ( trigger ) )
} )
. cloned ( )
. collect ( ) ;
2019-09-24 20:53:12 +00:00
self . matches . extend ( default_matches ) ;
2020-01-26 22:56:50 +00:00
// Merge global variables
let mut vars_name_set = HashSet ::new ( ) ;
self . global_vars . iter ( ) . for_each ( | m | {
vars_name_set . insert ( m . name . clone ( ) ) ;
} ) ;
2020-05-10 16:01:04 +00:00
let default_vars : Vec < MatchVariable > = default
. global_vars
. iter ( )
. filter ( | & m | ! vars_name_set . contains ( & m . name ) )
. cloned ( )
. collect ( ) ;
2020-01-26 22:56:50 +00:00
self . global_vars . extend ( default_vars ) ;
2019-09-24 20:53:12 +00:00
}
2019-09-09 15:59:44 +00:00
}
#[ derive(Clone, Debug, Serialize, Deserialize) ]
pub struct ConfigSet {
2019-09-14 08:30:51 +00:00
pub default : Configs ,
pub specific : Vec < Configs > ,
2019-09-09 15:59:44 +00:00
}
2019-09-10 20:53:45 +00:00
impl ConfigSet {
2019-10-08 22:37:42 +00:00
pub fn load ( config_dir : & Path , package_dir : & Path ) -> Result < ConfigSet , ConfigLoadError > {
if ! config_dir . is_dir ( ) {
2020-05-10 16:01:04 +00:00
return Err ( ConfigLoadError ::InvalidConfigDirectory ) ;
2019-09-10 20:53:45 +00:00
}
2019-09-09 15:59:44 +00:00
2019-09-23 21:34:55 +00:00
// Load default configuration
2019-10-08 22:37:42 +00:00
let default_file = config_dir . join ( DEFAULT_CONFIG_FILE_NAME ) ;
2019-09-10 20:53:45 +00:00
let default = Configs ::load_config ( default_file . as_path ( ) ) ? ;
2019-09-09 15:59:44 +00:00
2020-03-08 19:17:47 +00:00
// Check that a compatible backend is used, otherwise warn the user
if cfg! ( not ( target_os = " linux " ) ) & & default . backend = = BackendType ::Auto {
eprintln! ( " Warning: Using Auto backend is only supported on Linux, falling back to Inject backend. " ) ;
}
2020-05-26 19:18:27 +00:00
// Analyze which config files have to be loaded
2019-09-09 15:59:44 +00:00
2019-09-24 20:53:12 +00:00
let mut target_files = Vec ::new ( ) ;
2019-09-09 15:59:44 +00:00
2019-10-08 22:37:42 +00:00
let specific_dir = config_dir . join ( USER_CONFIGS_FOLDER_NAME ) ;
2019-09-23 21:34:55 +00:00
if specific_dir . exists ( ) {
2019-09-24 20:53:12 +00:00
let dir_entry = WalkDir ::new ( specific_dir ) ;
target_files . extend ( dir_entry ) ;
}
2019-09-09 15:59:44 +00:00
2020-05-26 19:18:27 +00:00
let package_files = if package_dir . exists ( ) {
2019-09-24 20:53:12 +00:00
let dir_entry = WalkDir ::new ( package_dir ) ;
2020-05-26 19:18:27 +00:00
dir_entry . into_iter ( ) . collect ( )
} else {
vec! [ ]
} ;
2019-09-09 15:59:44 +00:00
2019-09-24 20:53:12 +00:00
// Load the user defined config files
2019-09-15 13:51:29 +00:00
2019-09-24 20:53:12 +00:00
let mut name_set = HashSet ::new ( ) ;
let mut children_map : HashMap < String , Vec < Configs > > = HashMap ::new ( ) ;
2020-05-26 19:18:27 +00:00
let mut package_map : HashMap < String , Vec < Configs > > = HashMap ::new ( ) ;
2019-09-24 20:53:12 +00:00
let mut root_configs = Vec ::new ( ) ;
root_configs . push ( default ) ;
2019-09-09 15:59:44 +00:00
2020-05-26 19:18:27 +00:00
let mut file_loader = | entry : walkdir ::Result < DirEntry > ,
dest_map : & mut HashMap < String , Vec < Configs > > |
-> Result < ( ) , ConfigLoadError > {
match entry {
Ok ( entry ) = > {
let path = entry . path ( ) ;
// Skip non-yaml config files
if path
. extension ( )
. unwrap_or_default ( )
. to_str ( )
. unwrap_or_default ( )
! = " yml "
{
return Ok ( ( ) ) ;
}
2019-09-09 15:59:44 +00:00
2020-05-26 19:18:27 +00:00
// Skip hidden files
if path
. file_name ( )
. unwrap_or_default ( )
. to_str ( )
. unwrap_or_default ( )
. starts_with ( " . " )
{
return Ok ( ( ) ) ;
}
2020-04-15 16:47:59 +00:00
2020-05-26 19:18:27 +00:00
let mut config = Configs ::load_config ( & path ) ? ;
2019-09-09 15:59:44 +00:00
2020-05-26 19:18:27 +00:00
// Make sure the config does not contain reserved fields
if ! config . validate_user_defined_config ( ) {
return Err ( ConfigLoadError ::InvalidParameter ( path . to_owned ( ) ) ) ;
}
2019-09-14 20:14:39 +00:00
2020-05-26 19:18:27 +00:00
// No name specified, defaulting to the path name
if config . name = = " default " {
config . name = path . to_str ( ) . unwrap_or_default ( ) . to_owned ( ) ;
}
2019-09-24 20:53:12 +00:00
2020-05-26 19:18:27 +00:00
if name_set . contains ( & config . name ) {
return Err ( ConfigLoadError ::NameDuplicate ( path . to_owned ( ) ) ) ;
}
2019-09-10 21:32:43 +00:00
2020-05-26 19:18:27 +00:00
name_set . insert ( config . name . clone ( ) ) ;
2019-09-23 21:34:55 +00:00
2020-05-26 19:18:27 +00:00
if config . parent = = " self " {
// No parent, root config
root_configs . push ( config ) ;
} else {
// Children config
let children_vec = dest_map . entry ( config . parent . clone ( ) ) . or_default ( ) ;
children_vec . push ( config ) ;
}
}
Err ( e ) = > {
eprintln! ( " Warning: Unable to read config file: {} " , e ) ;
2019-09-23 21:34:55 +00:00
}
2019-09-24 20:53:12 +00:00
}
2020-05-26 19:18:27 +00:00
Ok ( ( ) )
} ;
// Load the default and user specific configs
for entry in target_files {
file_loader ( entry , & mut children_map ) ? ;
}
// Load the package related configs
for entry in package_files {
file_loader ( entry , & mut package_map ) ? ;
2019-09-24 20:53:12 +00:00
}
// Merge the children config files
2020-05-26 19:18:27 +00:00
let mut configs_without_packages = Vec ::new ( ) ;
2019-09-24 20:53:12 +00:00
for root_config in root_configs {
2020-05-26 19:18:27 +00:00
let config = ConfigSet ::reduce_configs ( root_config , & children_map , true ) ;
configs_without_packages . push ( config ) ;
}
// Merge package files
// Note: we need two different steps as the packages have a lower priority
// than configs.
let mut configs = Vec ::new ( ) ;
for root_config in configs_without_packages {
let config = ConfigSet ::reduce_configs ( root_config , & package_map , false ) ;
2019-09-24 20:53:12 +00:00
configs . push ( config ) ;
}
// Separate default from specific
2020-05-10 16:01:04 +00:00
let default = configs . get ( 0 ) . unwrap ( ) . clone ( ) ;
2019-09-24 20:53:12 +00:00
let mut specific = ( & configs [ 1 .. ] ) . to_vec ( ) . clone ( ) ;
2020-01-26 22:56:50 +00:00
// Add default entries to specific configs when needed
2019-09-24 20:53:12 +00:00
for config in specific . iter_mut ( ) {
2020-01-26 22:56:50 +00:00
if ! config . exclude_default_entries {
2020-05-26 19:18:27 +00:00
config . merge_no_overwrite ( & default ) ;
2019-09-10 20:53:45 +00:00
}
2019-09-09 15:59:44 +00:00
}
2020-01-21 20:58:10 +00:00
// Check if some triggers are conflicting with each other
// For more information, see: https://github.com/federico-terzi/espanso/issues/135
if default . conflict_check {
2020-04-03 18:10:06 +00:00
let has_conflicts = Self ::has_conflicts ( & default , & specific ) ;
if has_conflicts {
eprintln! ( " Warning: some triggers had conflicts and may not behave as intended " ) ;
2020-05-10 16:01:04 +00:00
eprintln! (
" To turn off this check, add \" conflict_check: false \" in the configuration "
) ;
2020-01-21 20:58:10 +00:00
}
}
2020-05-10 16:01:04 +00:00
Ok ( ConfigSet { default , specific } )
2019-09-09 15:59:44 +00:00
}
2020-05-26 19:18:27 +00:00
fn reduce_configs (
target : Configs ,
children_map : & HashMap < String , Vec < Configs > > ,
higher_priority : bool ,
) -> Configs {
2019-09-24 20:53:12 +00:00
if children_map . contains_key ( & target . name ) {
let mut target = target ;
for children in children_map . get ( & target . name ) . unwrap ( ) {
2020-05-26 19:18:27 +00:00
let children =
Self ::reduce_configs ( children . clone ( ) , children_map , higher_priority ) ;
if higher_priority {
target . merge_overwrite ( children ) ;
} else {
target . merge_no_overwrite ( & children ) ;
}
2019-09-24 20:53:12 +00:00
}
target
2020-05-10 16:01:04 +00:00
} else {
2019-09-24 20:53:12 +00:00
target
}
}
2019-09-10 20:53:45 +00:00
pub fn load_default ( ) -> Result < ConfigSet , ConfigLoadError > {
2019-10-08 22:37:42 +00:00
// Configuration related
2019-09-25 18:52:21 +00:00
2019-10-08 22:37:42 +00:00
let config_dir = crate ::context ::get_config_dir ( ) ;
2019-09-09 15:59:44 +00:00
2019-10-08 22:37:42 +00:00
let default_file = config_dir . join ( DEFAULT_CONFIG_FILE_NAME ) ;
2019-09-23 21:34:55 +00:00
2019-10-08 22:37:42 +00:00
// If config file does not exist, create one from template
if ! default_file . exists ( ) {
let result = fs ::write ( & default_file , DEFAULT_CONFIG_FILE_CONTENT ) ;
if result . is_err ( ) {
2020-05-10 16:01:04 +00:00
return Err ( ConfigLoadError ::UnableToCreateDefaultConfig ) ;
2019-09-25 18:52:21 +00:00
}
2019-10-08 22:37:42 +00:00
}
2019-09-23 21:34:55 +00:00
2019-10-08 22:37:42 +00:00
// Create auxiliary directories
2019-09-23 21:34:55 +00:00
2019-10-08 22:37:42 +00:00
let user_config_dir = config_dir . join ( USER_CONFIGS_FOLDER_NAME ) ;
if ! user_config_dir . exists ( ) {
let res = create_dir_all ( user_config_dir . as_path ( ) ) ;
if res . is_err ( ) {
2020-05-10 16:01:04 +00:00
return Err ( ConfigLoadError ::UnableToCreateDefaultConfig ) ;
2019-09-09 15:59:44 +00:00
}
}
2019-10-08 22:37:42 +00:00
// Packages
2019-09-25 18:52:21 +00:00
2019-10-08 22:37:42 +00:00
let package_dir = crate ::context ::get_package_dir ( ) ;
let res = create_dir_all ( package_dir . as_path ( ) ) ;
if res . is_err ( ) {
2020-05-10 16:01:04 +00:00
return Err ( ConfigLoadError ::UnableToCreateDefaultConfig ) ; // TODO: change error type
2019-10-08 22:37:42 +00:00
}
return ConfigSet ::load ( config_dir . as_path ( ) , package_dir . as_path ( ) ) ;
2019-09-25 18:52:21 +00:00
}
2020-01-21 20:58:10 +00:00
fn has_conflicts ( default : & Configs , specific : & Vec < Configs > ) -> bool {
2020-05-10 16:01:04 +00:00
let mut sorted_triggers : Vec < String > = default
. matches
. iter ( )
. flat_map ( | t | t . triggers . clone ( ) )
. collect ( ) ;
2020-01-21 20:58:10 +00:00
sorted_triggers . sort ( ) ;
let mut has_conflicts = Self ::list_has_conflicts ( & sorted_triggers ) ;
for s in specific . iter ( ) {
2020-05-10 16:01:04 +00:00
let mut specific_triggers : Vec < String > =
s . matches . iter ( ) . flat_map ( | t | t . triggers . clone ( ) ) . collect ( ) ;
2020-01-21 22:11:14 +00:00
specific_triggers . sort ( ) ;
2020-01-21 20:58:10 +00:00
has_conflicts | = Self ::list_has_conflicts ( & specific_triggers ) ;
}
has_conflicts
}
fn list_has_conflicts ( sorted_list : & Vec < String > ) -> bool {
if sorted_list . len ( ) < = 1 {
2020-05-10 16:01:04 +00:00
return false ;
2020-01-21 20:58:10 +00:00
}
let mut has_conflicts = false ;
for ( i , item ) in sorted_list . iter ( ) . skip ( 1 ) . enumerate ( ) {
let previous = & sorted_list [ i ] ;
if item . starts_with ( previous ) {
has_conflicts = true ;
2020-05-10 16:01:04 +00:00
eprintln! (
" Warning: trigger '{}' is conflicting with '{}' and may not behave as intended " ,
item , previous
) ;
2020-01-21 20:58:10 +00:00
}
}
has_conflicts
}
2019-09-09 15:59:44 +00:00
}
pub trait ConfigManager < ' a > {
fn active_config ( & ' a self ) -> & ' a Configs ;
fn default_config ( & ' a self ) -> & ' a Configs ;
fn matches ( & ' a self ) -> & ' a Vec < Match > ;
2019-09-10 20:53:45 +00:00
}
// Error handling
#[ derive(Debug, PartialEq) ]
pub enum ConfigLoadError {
FileNotFound ,
UnableToReadFile ,
InvalidYAML ( PathBuf , String ) ,
InvalidConfigDirectory ,
InvalidParameter ( PathBuf ) ,
NameDuplicate ( PathBuf ) ,
UnableToCreateDefaultConfig ,
}
impl fmt ::Display for ConfigLoadError {
fn fmt ( & self , f : & mut fmt ::Formatter ) -> fmt ::Result {
match self {
ConfigLoadError ::FileNotFound = > write! ( f , " File not found " ) ,
ConfigLoadError ::UnableToReadFile = > write! ( f , " Unable to read config file " ) ,
ConfigLoadError ::InvalidYAML ( path , e ) = > write! ( f , " Error parsing YAML file '{}', invalid syntax: {} " , path . to_str ( ) . unwrap_or_default ( ) , e ) ,
ConfigLoadError ::InvalidConfigDirectory = > write! ( f , " Invalid config directory " ) ,
2019-09-23 21:34:55 +00:00
ConfigLoadError ::InvalidParameter ( path ) = > write! ( f , " Invalid parameter in '{}', use of reserved parameters in used defined configs is not permitted " , path . to_str ( ) . unwrap_or_default ( ) ) ,
2019-09-10 20:53:45 +00:00
ConfigLoadError ::NameDuplicate ( path ) = > write! ( f , " Found duplicate 'name' in '{}', please use different names " , path . to_str ( ) . unwrap_or_default ( ) ) ,
ConfigLoadError ::UnableToCreateDefaultConfig = > write! ( f , " Could not generate default config file " ) ,
}
}
}
impl Error for ConfigLoadError {
fn description ( & self ) -> & str {
match self {
ConfigLoadError ::FileNotFound = > " File not found " ,
ConfigLoadError ::UnableToReadFile = > " Unable to read config file " ,
ConfigLoadError ::InvalidYAML ( _ , _ ) = > " Error parsing YAML file, invalid syntax " ,
ConfigLoadError ::InvalidConfigDirectory = > " Invalid config directory " ,
2019-09-23 21:34:55 +00:00
ConfigLoadError ::InvalidParameter ( _ ) = > " Invalid parameter, use of reserved parameters in user defined configs is not permitted " ,
2019-09-10 20:53:45 +00:00
ConfigLoadError ::NameDuplicate ( _ ) = > " Found duplicate 'name' in some configurations, please use different names " ,
ConfigLoadError ::UnableToCreateDefaultConfig = > " Could not generate default config file " ,
}
}
}
#[ cfg(test) ]
mod tests {
use super ::* ;
2020-05-10 16:01:04 +00:00
use crate ::matcher ::MatchContentType ;
2019-09-10 20:53:45 +00:00
use std ::io ::Write ;
use tempfile ::{ NamedTempFile , TempDir } ;
2020-05-10 16:01:04 +00:00
const TEST_WORKING_CONFIG_FILE : & str = include_str! ( " ../res/test/working_config.yml " ) ;
const TEST_CONFIG_FILE_WITH_BAD_YAML : & str =
include_str! ( " ../res/test/config_with_bad_yaml.yml " ) ;
2019-09-10 20:53:45 +00:00
// Test Configs
fn create_tmp_file ( string : & str ) -> NamedTempFile {
let file = NamedTempFile ::new ( ) . unwrap ( ) ;
2020-04-03 18:10:06 +00:00
file . as_file ( ) . write_all ( string . as_bytes ( ) ) . unwrap ( ) ;
2019-09-10 20:53:45 +00:00
file
}
2019-09-13 09:50:39 +00:00
fn variant_eq < T > ( a : & T , b : & T ) -> bool {
std ::mem ::discriminant ( a ) = = std ::mem ::discriminant ( b )
}
2019-09-10 20:53:45 +00:00
#[ test ]
fn test_config_file_not_found ( ) {
let config = Configs ::load_config ( Path ::new ( " invalid/path " ) ) ;
assert_eq! ( config . is_err ( ) , true ) ;
assert_eq! ( config . unwrap_err ( ) , ConfigLoadError ::FileNotFound ) ;
}
#[ test ]
fn test_config_file_with_bad_yaml_syntax ( ) {
let broken_config_file = create_tmp_file ( TEST_CONFIG_FILE_WITH_BAD_YAML ) ;
let config = Configs ::load_config ( broken_config_file . path ( ) ) ;
match config {
2020-05-10 16:01:04 +00:00
Ok ( _ ) = > assert! ( false ) ,
2019-09-10 20:53:45 +00:00
Err ( e ) = > {
match e {
2020-05-10 16:01:04 +00:00
ConfigLoadError ::InvalidYAML ( p , _ ) = > {
assert_eq! ( p , broken_config_file . path ( ) . to_owned ( ) )
}
2019-09-10 20:53:45 +00:00
_ = > assert! ( false ) ,
}
assert! ( true ) ;
2020-05-10 16:01:04 +00:00
}
2019-09-10 20:53:45 +00:00
}
}
#[ test ]
fn test_validate_field_macro ( ) {
let mut result = true ;
validate_field! ( result , 3 , 3 ) ;
assert_eq! ( result , true ) ;
validate_field! ( result , 10 , 3 ) ;
assert_eq! ( result , false ) ;
validate_field! ( result , 3 , 3 ) ;
assert_eq! ( result , false ) ;
}
#[ test ]
2019-09-23 21:34:55 +00:00
fn test_user_defined_config_does_not_have_reserved_fields ( ) {
2020-05-10 16:01:04 +00:00
let working_config_file = create_tmp_file (
r ###"
2019-09-10 20:53:45 +00:00
backend : Clipboard
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-10 20:53:45 +00:00
let config = Configs ::load_config ( working_config_file . path ( ) ) ;
2019-09-23 21:34:55 +00:00
assert_eq! ( config . unwrap ( ) . validate_user_defined_config ( ) , true ) ;
2019-09-10 20:53:45 +00:00
}
#[ test ]
2019-09-23 21:34:55 +00:00
fn test_user_defined_config_has_reserved_fields_config_caching_interval ( ) {
2020-05-10 16:01:04 +00:00
let working_config_file = create_tmp_file (
r ###"
2019-09-10 20:53:45 +00:00
# This should not happen in an app - specific config
config_caching_interval : 100
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-10 20:53:45 +00:00
let config = Configs ::load_config ( working_config_file . path ( ) ) ;
2019-09-23 21:34:55 +00:00
assert_eq! ( config . unwrap ( ) . validate_user_defined_config ( ) , false ) ;
2019-09-10 20:53:45 +00:00
}
#[ test ]
2019-09-23 21:34:55 +00:00
fn test_user_defined_config_has_reserved_fields_toggle_key ( ) {
2020-05-10 16:01:04 +00:00
let working_config_file = create_tmp_file (
r ###"
2019-09-10 20:53:45 +00:00
# This should not happen in an app - specific config
toggle_key : CTRL
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-10 20:53:45 +00:00
let config = Configs ::load_config ( working_config_file . path ( ) ) ;
2019-09-23 21:34:55 +00:00
assert_eq! ( config . unwrap ( ) . validate_user_defined_config ( ) , false ) ;
2019-09-10 20:53:45 +00:00
}
#[ test ]
2019-09-23 21:34:55 +00:00
fn test_user_defined_config_has_reserved_fields_toggle_interval ( ) {
2020-05-10 16:01:04 +00:00
let working_config_file = create_tmp_file (
r ###"
2019-09-10 20:53:45 +00:00
# This should not happen in an app - specific config
toggle_interval : 1000
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-10 20:53:45 +00:00
let config = Configs ::load_config ( working_config_file . path ( ) ) ;
2019-09-23 21:34:55 +00:00
assert_eq! ( config . unwrap ( ) . validate_user_defined_config ( ) , false ) ;
2019-09-10 20:53:45 +00:00
}
#[ test ]
2019-09-23 21:34:55 +00:00
fn test_user_defined_config_has_reserved_fields_backspace_limit ( ) {
2020-05-10 16:01:04 +00:00
let working_config_file = create_tmp_file (
r ###"
2019-09-10 20:53:45 +00:00
# This should not happen in an app - specific config
backspace_limit : 10
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-10 20:53:45 +00:00
let config = Configs ::load_config ( working_config_file . path ( ) ) ;
2019-09-23 21:34:55 +00:00
assert_eq! ( config . unwrap ( ) . validate_user_defined_config ( ) , false ) ;
2019-09-10 20:53:45 +00:00
}
#[ test ]
fn test_config_loaded_correctly ( ) {
let working_config_file = create_tmp_file ( TEST_WORKING_CONFIG_FILE ) ;
let config = Configs ::load_config ( working_config_file . path ( ) ) ;
assert_eq! ( config . is_ok ( ) , true ) ;
}
// Test ConfigSet
2019-10-08 22:37:42 +00:00
pub fn create_temp_espanso_directories ( ) -> ( TempDir , TempDir ) {
create_temp_espanso_directories_with_default_content ( DEFAULT_CONFIG_FILE_CONTENT )
}
2020-05-10 16:01:04 +00:00
pub fn create_temp_espanso_directories_with_default_content (
default_content : & str ,
) -> ( TempDir , TempDir ) {
2019-10-08 22:37:42 +00:00
let data_dir = TempDir ::new ( ) . expect ( " unable to create data directory " ) ;
let package_dir = TempDir ::new ( ) . expect ( " unable to create package directory " ) ;
let default_path = data_dir . path ( ) . join ( DEFAULT_CONFIG_FILE_NAME ) ;
2020-04-03 18:10:06 +00:00
fs ::write ( default_path , default_content ) . unwrap ( ) ;
2019-10-08 22:37:42 +00:00
( data_dir , package_dir )
}
pub fn create_temp_file_in_dir ( tmp_dir : & PathBuf , name : & str , content : & str ) -> PathBuf {
let user_defined_path = tmp_dir . join ( name ) ;
let user_defined_path_copy = user_defined_path . clone ( ) ;
2020-04-03 18:10:06 +00:00
fs ::write ( user_defined_path , content ) . unwrap ( ) ;
2019-10-08 22:37:42 +00:00
user_defined_path_copy
}
pub fn create_user_config_file ( tmp_dir : & Path , name : & str , content : & str ) -> PathBuf {
let user_config_dir = tmp_dir . join ( USER_CONFIGS_FOLDER_NAME ) ;
if ! user_config_dir . exists ( ) {
2020-04-03 18:10:06 +00:00
create_dir_all ( & user_config_dir ) . unwrap ( ) ;
2019-10-08 22:37:42 +00:00
}
create_temp_file_in_dir ( & user_config_dir , name , content )
}
2020-05-10 16:01:04 +00:00
pub fn create_package_file (
package_data_dir : & Path ,
package_name : & str ,
filename : & str ,
content : & str ,
) -> PathBuf {
2019-10-08 22:37:42 +00:00
let package_dir = package_data_dir . join ( package_name ) ;
if ! package_dir . exists ( ) {
2020-04-03 18:10:06 +00:00
create_dir_all ( & package_dir ) . unwrap ( ) ;
2019-10-08 22:37:42 +00:00
}
create_temp_file_in_dir ( & package_dir , filename , content )
}
2019-09-10 20:53:45 +00:00
#[ test ]
fn test_config_set_default_content_should_work_correctly ( ) {
2019-10-08 22:37:42 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories ( ) ;
2019-09-10 20:53:45 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) ;
2019-09-10 20:53:45 +00:00
assert! ( config_set . is_ok ( ) ) ;
}
#[ test ]
fn test_config_set_load_fail_bad_directory ( ) {
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( Path ::new ( " invalid/path " ) , Path ::new ( " invalid/path " ) ) ;
2019-09-10 20:53:45 +00:00
assert_eq! ( config_set . is_err ( ) , true ) ;
2020-05-10 16:01:04 +00:00
assert_eq! (
config_set . unwrap_err ( ) ,
ConfigLoadError ::InvalidConfigDirectory
) ;
2019-09-10 20:53:45 +00:00
}
#[ test ]
fn test_config_set_missing_default_file ( ) {
2019-10-08 22:37:42 +00:00
let data_dir = TempDir ::new ( ) . expect ( " unable to create temp directory " ) ;
let package_dir = TempDir ::new ( ) . expect ( " unable to create package directory " ) ;
2019-09-10 20:53:45 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) ;
2019-09-10 20:53:45 +00:00
assert_eq! ( config_set . is_err ( ) , true ) ;
assert_eq! ( config_set . unwrap_err ( ) , ConfigLoadError ::FileNotFound ) ;
}
#[ test ]
fn test_config_set_invalid_yaml_syntax ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) =
create_temp_espanso_directories_with_default_content ( TEST_CONFIG_FILE_WITH_BAD_YAML ) ;
2019-10-08 22:37:42 +00:00
let default_path = data_dir . path ( ) . join ( DEFAULT_CONFIG_FILE_NAME ) ;
2019-09-10 20:53:45 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) ;
2019-09-10 20:53:45 +00:00
match config_set {
2020-05-10 16:01:04 +00:00
Ok ( _ ) = > assert! ( false ) ,
2019-09-10 20:53:45 +00:00
Err ( e ) = > {
match e {
2019-10-08 22:37:42 +00:00
ConfigLoadError ::InvalidYAML ( p , _ ) = > assert_eq! ( p , default_path ) ,
2019-09-10 20:53:45 +00:00
_ = > assert! ( false ) ,
}
assert! ( true ) ;
2020-05-10 16:01:04 +00:00
}
2019-09-10 20:53:45 +00:00
}
}
#[ test ]
fn test_config_set_specific_file_with_reserved_fields ( ) {
2019-10-08 22:37:42 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories ( ) ;
2019-09-10 20:53:45 +00:00
2020-05-10 16:01:04 +00:00
let user_defined_path = create_user_config_file (
data_dir . path ( ) ,
" specific.yml " ,
r ###"
2019-09-10 20:53:45 +00:00
config_caching_interval : 10000
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-23 21:34:55 +00:00
let user_defined_path_copy = user_defined_path . clone ( ) ;
2019-09-10 20:53:45 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) ;
2019-09-10 20:53:45 +00:00
assert! ( config_set . is_err ( ) ) ;
2020-05-10 16:01:04 +00:00
assert_eq! (
config_set . unwrap_err ( ) ,
ConfigLoadError ::InvalidParameter ( user_defined_path_copy )
)
2019-09-10 20:53:45 +00:00
}
#[ test ]
2019-09-24 20:53:12 +00:00
fn test_config_set_specific_file_missing_name_auto_generated ( ) {
2019-10-08 22:37:42 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories ( ) ;
2019-09-10 20:53:45 +00:00
2020-05-10 16:01:04 +00:00
let user_defined_path = create_user_config_file (
data_dir . path ( ) ,
" specific.yml " ,
r ###"
2019-09-10 20:53:45 +00:00
backend : Clipboard
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-23 21:34:55 +00:00
let user_defined_path_copy = user_defined_path . clone ( ) ;
2019-09-10 20:53:45 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) ;
2019-09-24 20:53:12 +00:00
assert! ( config_set . is_ok ( ) ) ;
2020-05-10 16:01:04 +00:00
assert_eq! (
config_set . unwrap ( ) . specific [ 0 ] . name ,
user_defined_path_copy . to_str ( ) . unwrap_or_default ( )
)
2019-09-10 20:53:45 +00:00
}
2019-09-10 21:32:43 +00:00
#[ test ]
fn test_config_set_specific_file_duplicate_name ( ) {
2019-10-08 22:37:42 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories ( ) ;
2019-09-10 21:32:43 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific.yml " ,
r ###"
2019-09-10 20:53:45 +00:00
name : specific1
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-10 20:53:45 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific2.yml " ,
r ###"
2019-09-10 20:53:45 +00:00
name : specific1
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-10 20:53:45 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) ;
2019-09-10 20:53:45 +00:00
assert! ( config_set . is_err ( ) ) ;
2020-05-10 16:01:04 +00:00
assert! ( variant_eq (
& config_set . unwrap_err ( ) ,
& ConfigLoadError ::NameDuplicate ( PathBuf ::new ( ) )
) )
2019-09-10 20:53:45 +00:00
}
2019-09-14 20:14:39 +00:00
#[ test ]
2019-09-23 21:34:55 +00:00
fn test_user_defined_config_set_merge_with_parent_matches ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2019-09-14 20:14:39 +00:00
matches :
- trigger : " :lol "
replace : " LOL "
- trigger : " :yess "
replace : " Bob "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-14 20:14:39 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific1.yml " ,
r ###"
2019-09-14 20:14:39 +00:00
name : specific1
matches :
- trigger : " hello "
replace : " newstring "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-14 20:14:39 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2019-09-14 20:14:39 +00:00
assert_eq! ( config_set . default . matches . len ( ) , 2 ) ;
assert_eq! ( config_set . specific [ 0 ] . matches . len ( ) , 3 ) ;
2020-05-10 16:01:04 +00:00
assert! ( config_set . specific [ 0 ]
. matches
. iter ( )
. find ( | x | x . triggers [ 0 ] = = " hello " )
. is_some ( ) ) ;
assert! ( config_set . specific [ 0 ]
. matches
. iter ( )
. find ( | x | x . triggers [ 0 ] = = " :lol " )
. is_some ( ) ) ;
assert! ( config_set . specific [ 0 ]
. matches
. iter ( )
. find ( | x | x . triggers [ 0 ] = = " :yess " )
. is_some ( ) ) ;
2019-09-14 20:14:39 +00:00
}
#[ test ]
2019-09-23 21:34:55 +00:00
fn test_user_defined_config_set_merge_with_parent_matches_child_priority ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2019-09-14 20:14:39 +00:00
matches :
- trigger : " :lol "
replace : " LOL "
- trigger : " :yess "
replace : " Bob "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-14 20:14:39 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific2.yml " ,
r ###"
2019-09-14 20:14:39 +00:00
name : specific1
matches :
- trigger : " :lol "
replace : " newstring "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-14 20:14:39 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2019-09-14 20:14:39 +00:00
assert_eq! ( config_set . default . matches . len ( ) , 2 ) ;
assert_eq! ( config_set . specific [ 0 ] . matches . len ( ) , 2 ) ;
2020-05-10 16:01:04 +00:00
assert! ( config_set . specific [ 0 ]
. matches
. iter ( )
. find ( | x | {
if let MatchContentType ::Text ( content ) = & x . content {
x . triggers [ 0 ] = = " :lol " & & content . replace = = " newstring "
} else {
false
}
} )
. is_some ( ) ) ;
assert! ( config_set . specific [ 0 ]
. matches
. iter ( )
. find ( | x | x . triggers [ 0 ] = = " :yess " )
. is_some ( ) ) ;
2019-09-14 20:14:39 +00:00
}
#[ test ]
2019-09-23 21:34:55 +00:00
fn test_user_defined_config_set_exclude_merge_with_parent_matches ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2019-09-14 20:14:39 +00:00
matches :
- trigger : " :lol "
replace : " LOL "
- trigger : " :yess "
replace : " Bob "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-14 20:14:39 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific2.yml " ,
r ###"
2019-09-14 20:14:39 +00:00
name : specific1
2020-01-26 22:56:50 +00:00
exclude_default_entries : true
2019-09-14 20:14:39 +00:00
matches :
- trigger : " hello "
replace : " newstring "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-14 20:14:39 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2019-09-14 20:14:39 +00:00
assert_eq! ( config_set . default . matches . len ( ) , 2 ) ;
assert_eq! ( config_set . specific [ 0 ] . matches . len ( ) , 1 ) ;
2020-05-10 16:01:04 +00:00
assert! ( config_set . specific [ 0 ]
. matches
. iter ( )
. find ( | x | {
if let MatchContentType ::Text ( content ) = & x . content {
x . triggers [ 0 ] = = " hello " & & content . replace = = " newstring "
} else {
false
}
} )
. is_some ( ) ) ;
2019-09-14 20:14:39 +00:00
}
2019-09-15 13:51:29 +00:00
#[ test ]
fn test_only_yaml_files_are_loaded_from_config ( ) {
2019-10-08 22:37:42 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
matches :
- trigger : " :lol "
replace : " LOL "
- trigger : " :yess "
replace : " Bob "
2020-05-10 16:01:04 +00:00
" ###,
2019-10-08 22:37:42 +00:00
) ;
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific.zzz " ,
r ###"
2019-09-15 13:51:29 +00:00
name : specific1
2020-01-26 22:56:50 +00:00
exclude_default_entries : true
2019-09-15 13:51:29 +00:00
matches :
- trigger : " hello "
replace : " newstring "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-15 13:51:29 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2019-09-15 13:51:29 +00:00
assert_eq! ( config_set . specific . len ( ) , 0 ) ;
}
2019-09-24 20:53:12 +00:00
2020-04-15 16:47:59 +00:00
#[ test ]
fn test_hidden_files_are_ignored ( ) {
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
matches :
- trigger : " :lol "
replace : " LOL "
- trigger : " :yess "
replace : " Bob "
2020-05-10 16:01:04 +00:00
" ###,
2020-04-15 16:47:59 +00:00
) ;
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" .specific.yml " ,
r ###"
2020-04-15 16:47:59 +00:00
name : specific1
exclude_default_entries : true
matches :
- trigger : " hello "
replace : " newstring "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2020-04-15 16:47:59 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
assert_eq! ( config_set . specific . len ( ) , 0 ) ;
}
2019-09-24 20:53:12 +00:00
#[ test ]
fn test_config_set_no_parent_configs_works_correctly ( ) {
2019-10-08 22:37:42 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories ( ) ;
2019-09-24 20:53:12 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific.yml " ,
r ###"
2019-09-24 20:53:12 +00:00
name : specific1
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific2.yml " ,
r ###"
2019-09-24 20:53:12 +00:00
name : specific2
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2019-09-24 20:53:12 +00:00
assert_eq! ( config_set . specific . len ( ) , 2 ) ;
}
#[ test ]
fn test_config_set_default_parent_works_correctly ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2019-09-24 20:53:12 +00:00
matches :
- trigger : hasta
replace : Hasta la vista
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific.yml " ,
r ###"
2019-09-24 20:53:12 +00:00
parent : default
matches :
- trigger : " hello "
replace : " world "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2019-09-24 20:53:12 +00:00
assert_eq! ( config_set . specific . len ( ) , 0 ) ;
assert_eq! ( config_set . default . matches . len ( ) , 2 ) ;
2020-05-10 16:01:04 +00:00
assert! ( config_set
. default
. matches
. iter ( )
. any ( | m | m . triggers [ 0 ] = = " hasta " ) ) ;
assert! ( config_set
. default
. matches
. iter ( )
. any ( | m | m . triggers [ 0 ] = = " hello " ) ) ;
2019-09-24 20:53:12 +00:00
}
#[ test ]
fn test_config_set_no_parent_should_not_merge ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2019-09-24 20:53:12 +00:00
matches :
- trigger : hasta
replace : Hasta la vista
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific.yml " ,
r ###"
2019-09-24 20:53:12 +00:00
matches :
- trigger : " hello "
replace : " world "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2019-09-24 20:53:12 +00:00
assert_eq! ( config_set . specific . len ( ) , 1 ) ;
assert_eq! ( config_set . default . matches . len ( ) , 1 ) ;
2020-05-10 16:01:04 +00:00
assert! ( config_set
. default
. matches
. iter ( )
. any ( | m | m . triggers [ 0 ] = = " hasta " ) ) ;
assert! ( ! config_set
. default
. matches
. iter ( )
. any ( | m | m . triggers [ 0 ] = = " hello " ) ) ;
assert! ( config_set . specific [ 0 ]
. matches
. iter ( )
. any ( | m | m . triggers [ 0 ] = = " hello " ) ) ;
2019-09-24 20:53:12 +00:00
}
#[ test ]
fn test_config_set_default_nested_parent_works_correctly ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2019-09-24 20:53:12 +00:00
matches :
- trigger : hasta
replace : Hasta la vista
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific.yml " ,
r ###"
2019-09-24 20:53:12 +00:00
name : custom1
parent : default
matches :
- trigger : " hello "
replace : " world "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific2.yml " ,
r ###"
2019-09-24 20:53:12 +00:00
parent : custom1
matches :
- trigger : " super "
replace : " mario "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2019-09-24 20:53:12 +00:00
assert_eq! ( config_set . specific . len ( ) , 0 ) ;
assert_eq! ( config_set . default . matches . len ( ) , 3 ) ;
2020-05-10 16:01:04 +00:00
assert! ( config_set
. default
. matches
. iter ( )
. any ( | m | m . triggers [ 0 ] = = " hasta " ) ) ;
assert! ( config_set
. default
. matches
. iter ( )
. any ( | m | m . triggers [ 0 ] = = " hello " ) ) ;
assert! ( config_set
. default
. matches
. iter ( )
. any ( | m | m . triggers [ 0 ] = = " super " ) ) ;
2019-09-24 20:53:12 +00:00
}
#[ test ]
fn test_config_set_parent_merge_children_priority_should_be_higher ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2019-09-24 20:53:12 +00:00
matches :
- trigger : hasta
replace : Hasta la vista
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific.yml " ,
r ###"
2019-09-24 20:53:12 +00:00
parent : default
matches :
- trigger : " hasta "
replace : " world "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2019-09-24 20:53:12 +00:00
assert_eq! ( config_set . specific . len ( ) , 0 ) ;
assert_eq! ( config_set . default . matches . len ( ) , 1 ) ;
2019-11-28 21:56:00 +00:00
assert! ( config_set . default . matches . iter ( ) . any ( | m | {
if let MatchContentType ::Text ( content ) = & m . content {
2020-03-02 20:43:26 +00:00
m . triggers [ 0 ] = = " hasta " & & content . replace = = " world "
2020-05-10 16:01:04 +00:00
} else {
2019-11-28 21:56:00 +00:00
false
}
} ) ) ;
2019-09-24 20:53:12 +00:00
}
#[ test ]
fn test_config_set_package_configs_default_merge ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2019-09-24 20:53:12 +00:00
matches :
- trigger : hasta
replace : Hasta la vista
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2020-05-10 16:01:04 +00:00
create_package_file (
package_dir . path ( ) ,
" package1 " ,
" package.yml " ,
r ###"
2019-09-24 20:53:12 +00:00
parent : default
matches :
- trigger : " harry "
replace : " potter "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2019-09-24 20:53:12 +00:00
assert_eq! ( config_set . specific . len ( ) , 0 ) ;
assert_eq! ( config_set . default . matches . len ( ) , 2 ) ;
2020-05-10 16:01:04 +00:00
assert! ( config_set
. default
. matches
. iter ( )
. any ( | m | m . triggers [ 0 ] = = " hasta " ) ) ;
assert! ( config_set
. default
. matches
. iter ( )
. any ( | m | m . triggers [ 0 ] = = " harry " ) ) ;
2019-09-24 20:53:12 +00:00
}
2020-05-26 19:18:27 +00:00
#[ test ]
fn test_config_set_package_configs_lower_priority_than_user ( ) {
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
matches :
- trigger : hasta
replace : Hasta la vista
" ###,
) ;
create_package_file (
package_dir . path ( ) ,
" package1 " ,
" package.yml " ,
r ###"
parent : default
matches :
- trigger : " hasta "
replace : " potter "
" ###,
) ;
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
assert_eq! ( config_set . specific . len ( ) , 0 ) ;
assert_eq! ( config_set . default . matches . len ( ) , 1 ) ;
if let MatchContentType ::Text ( content ) = config_set . default . matches [ 0 ] . content . clone ( ) {
assert_eq! ( config_set . default . matches [ 0 ] . triggers [ 0 ] , " hasta " ) ;
assert_eq! ( content . replace , " Hasta la vista " )
} else {
panic! ( " invalid content " ) ;
}
}
2019-09-24 20:53:12 +00:00
#[ test ]
fn test_config_set_package_configs_without_merge ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2019-09-24 20:53:12 +00:00
matches :
- trigger : hasta
replace : Hasta la vista
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2020-05-10 16:01:04 +00:00
create_package_file (
package_dir . path ( ) ,
" package1 " ,
" package.yml " ,
r ###"
2019-09-24 20:53:12 +00:00
matches :
- trigger : " harry "
replace : " potter "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2019-09-24 20:53:12 +00:00
assert_eq! ( config_set . specific . len ( ) , 1 ) ;
assert_eq! ( config_set . default . matches . len ( ) , 1 ) ;
2020-05-10 16:01:04 +00:00
assert! ( config_set
. default
. matches
. iter ( )
. any ( | m | m . triggers [ 0 ] = = " hasta " ) ) ;
assert! ( config_set . specific [ 0 ]
. matches
. iter ( )
. any ( | m | m . triggers [ 0 ] = = " harry " ) ) ;
2019-09-24 20:53:12 +00:00
}
#[ test ]
fn test_config_set_package_configs_multiple_files ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2019-09-24 20:53:12 +00:00
matches :
- trigger : hasta
replace : Hasta la vista
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2020-05-10 16:01:04 +00:00
create_package_file (
package_dir . path ( ) ,
" package1 " ,
" package.yml " ,
r ###"
2019-09-24 20:53:12 +00:00
name : package1
matches :
- trigger : " harry "
replace : " potter "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2020-05-10 16:01:04 +00:00
create_package_file (
package_dir . path ( ) ,
" package1 " ,
" addon.yml " ,
r ###"
2019-09-24 20:53:12 +00:00
parent : package1
matches :
- trigger : " ron "
replace : " weasley "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2019-09-24 20:53:12 +00:00
2019-10-08 22:37:42 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2019-09-24 20:53:12 +00:00
assert_eq! ( config_set . specific . len ( ) , 1 ) ;
assert_eq! ( config_set . default . matches . len ( ) , 1 ) ;
2020-05-10 16:01:04 +00:00
assert! ( config_set
. default
. matches
. iter ( )
. any ( | m | m . triggers [ 0 ] = = " hasta " ) ) ;
assert! ( config_set . specific [ 0 ]
. matches
. iter ( )
. any ( | m | m . triggers [ 0 ] = = " harry " ) ) ;
assert! ( config_set . specific [ 0 ]
. matches
. iter ( )
. any ( | m | m . triggers [ 0 ] = = " ron " ) ) ;
2019-09-24 20:53:12 +00:00
}
2020-01-21 22:11:14 +00:00
#[ test ]
fn test_list_has_conflict_no_conflict ( ) {
2020-05-10 16:01:04 +00:00
assert_eq! (
ConfigSet ::list_has_conflicts ( & vec! ( " :ab " . to_owned ( ) , " :bc " . to_owned ( ) ) ) ,
false
) ;
2020-01-21 22:11:14 +00:00
}
#[ test ]
fn test_list_has_conflict_conflict ( ) {
2020-05-10 16:01:04 +00:00
let mut list = vec! [ " ac " . to_owned ( ) , " ab " . to_owned ( ) , " abc " . to_owned ( ) ] ;
2020-01-21 22:11:14 +00:00
list . sort ( ) ;
assert_eq! ( ConfigSet ::list_has_conflicts ( & list ) , true ) ;
}
#[ test ]
fn test_has_conflict_no_conflict ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2020-01-21 22:11:14 +00:00
matches :
- trigger : ac
replace : Hasta la vista
- trigger : bc
replace : Jon
2020-05-10 16:01:04 +00:00
" ###,
) ;
2020-01-21 22:11:14 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific.yml " ,
r ###"
2020-01-21 22:11:14 +00:00
name : specific1
matches :
- trigger : " hello "
replace : " world "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2020-01-21 22:11:14 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2020-05-10 16:01:04 +00:00
assert_eq! (
ConfigSet ::has_conflicts ( & config_set . default , & config_set . specific ) ,
false
) ;
2020-01-21 22:11:14 +00:00
}
#[ test ]
fn test_has_conflict_conflict_in_default ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2020-01-21 22:11:14 +00:00
matches :
- trigger : ac
replace : Hasta la vista
- trigger : bc
replace : Jon
- trigger : acb
replace : Error
2020-05-10 16:01:04 +00:00
" ###,
) ;
2020-01-21 22:11:14 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific.yml " ,
r ###"
2020-01-21 22:11:14 +00:00
name : specific1
matches :
- trigger : " hello "
replace : " world "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2020-01-21 22:11:14 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2020-05-10 16:01:04 +00:00
assert_eq! (
ConfigSet ::has_conflicts ( & config_set . default , & config_set . specific ) ,
true
) ;
2020-01-21 22:11:14 +00:00
}
#[ test ]
fn test_has_conflict_conflict_in_specific_and_default ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2020-01-21 22:11:14 +00:00
matches :
- trigger : ac
replace : Hasta la vista
- trigger : bc
replace : Jon
2020-05-10 16:01:04 +00:00
" ###,
) ;
2020-01-21 22:11:14 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific.yml " ,
r ###"
2020-01-21 22:11:14 +00:00
name : specific1
matches :
- trigger : " bcd "
replace : " Conflict "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2020-01-21 22:11:14 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2020-05-10 16:01:04 +00:00
assert_eq! (
ConfigSet ::has_conflicts ( & config_set . default , & config_set . specific ) ,
true
) ;
2020-01-21 22:11:14 +00:00
}
#[ test ]
fn test_has_conflict_no_conflict_in_specific_and_specific ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2020-01-21 22:11:14 +00:00
matches :
- trigger : ac
replace : Hasta la vista
- trigger : bc
replace : Jon
2020-05-10 16:01:04 +00:00
" ###,
) ;
2020-01-21 22:11:14 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific.yml " ,
r ###"
2020-01-21 22:11:14 +00:00
name : specific1
matches :
- trigger : " bad "
replace : " Conflict "
2020-05-10 16:01:04 +00:00
" ###,
) ;
create_user_config_file (
data_dir . path ( ) ,
" specific2.yml " ,
r ###"
2020-01-21 22:11:14 +00:00
name : specific2
matches :
- trigger : " badass "
replace : " Conflict "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2020-01-21 22:11:14 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
2020-05-10 16:01:04 +00:00
assert_eq! (
ConfigSet ::has_conflicts ( & config_set . default , & config_set . specific ) ,
false
) ;
2020-01-21 22:11:14 +00:00
}
2020-01-26 22:56:50 +00:00
#[ test ]
fn test_config_set_specific_inherits_default_global_vars ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2020-01-26 22:56:50 +00:00
global_vars :
- name : testvar
type : date
params :
format : " %m "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2020-01-26 22:56:50 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific.yml " ,
r ###"
2020-01-26 22:56:50 +00:00
global_vars :
- name : specificvar
type : date
params :
format : " %m "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2020-01-26 22:56:50 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
assert_eq! ( config_set . specific . len ( ) , 1 ) ;
assert_eq! ( config_set . default . global_vars . len ( ) , 1 ) ;
assert_eq! ( config_set . specific [ 0 ] . global_vars . len ( ) , 2 ) ;
2020-05-10 16:01:04 +00:00
assert! ( config_set . specific [ 0 ]
. global_vars
. iter ( )
. any ( | m | m . name = = " testvar " ) ) ;
assert! ( config_set . specific [ 0 ]
. global_vars
. iter ( )
. any ( | m | m . name = = " specificvar " ) ) ;
2020-01-26 22:56:50 +00:00
}
#[ test ]
fn test_config_set_default_get_variables_from_specific ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2020-01-26 22:56:50 +00:00
global_vars :
- name : testvar
type : date
params :
format : " %m "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2020-01-26 22:56:50 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific.yml " ,
r ###"
2020-01-26 22:56:50 +00:00
parent : default
global_vars :
- name : specificvar
type : date
params :
format : " %m "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2020-01-26 22:56:50 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
assert_eq! ( config_set . specific . len ( ) , 0 ) ;
assert_eq! ( config_set . default . global_vars . len ( ) , 2 ) ;
2020-05-10 16:01:04 +00:00
assert! ( config_set
. default
. global_vars
. iter ( )
. any ( | m | m . name = = " testvar " ) ) ;
assert! ( config_set
. default
. global_vars
. iter ( )
. any ( | m | m . name = = " specificvar " ) ) ;
2020-01-26 22:56:50 +00:00
}
#[ test ]
fn test_config_set_specific_dont_inherits_default_global_vars_when_exclude_is_on ( ) {
2020-05-10 16:01:04 +00:00
let ( data_dir , package_dir ) = create_temp_espanso_directories_with_default_content (
r ###"
2020-01-26 22:56:50 +00:00
global_vars :
- name : testvar
type : date
params :
format : " %m "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2020-01-26 22:56:50 +00:00
2020-05-10 16:01:04 +00:00
create_user_config_file (
data_dir . path ( ) ,
" specific.yml " ,
r ###"
2020-01-26 22:56:50 +00:00
exclude_default_entries : true
global_vars :
- name : specificvar
type : date
params :
format : " %m "
2020-05-10 16:01:04 +00:00
" ###,
) ;
2020-01-26 22:56:50 +00:00
let config_set = ConfigSet ::load ( data_dir . path ( ) , package_dir . path ( ) ) . unwrap ( ) ;
assert_eq! ( config_set . specific . len ( ) , 1 ) ;
assert_eq! ( config_set . default . global_vars . len ( ) , 1 ) ;
assert_eq! ( config_set . specific [ 0 ] . global_vars . len ( ) , 1 ) ;
2020-05-10 16:01:04 +00:00
assert! ( config_set . specific [ 0 ]
. global_vars
. iter ( )
. any ( | m | m . name = = " specificvar " ) ) ;
2020-01-26 22:56:50 +00:00
}
2020-05-10 16:01:04 +00:00
}