Change default config structure

This commit is contained in:
Federico Terzi 2019-09-23 23:34:55 +02:00
parent a0b0842273
commit efc6a9c860
4 changed files with 139 additions and 116 deletions

View File

@ -37,6 +37,8 @@ pub(crate) mod runtime;
const DEFAULT_CONFIG_FILE_CONTENT : &str = include_str!("../res/config.yml");
const DEFAULT_CONFIG_FILE_NAME : &str = "default.yml";
const USER_CONFIGS_FOLDER_NAME: &str = "user";
const PACKAGES_FOLDER_NAME : &str = "packages";
// Default values for primitives
fn default_name() -> String{ "default".to_owned() }
@ -119,10 +121,10 @@ macro_rules! validate_field {
impl Configs {
/*
* Validate the Config instance.
* It makes sure that app-specific config instances do not define
* It makes sure that user defined config instances do not define
* attributes reserved to the default config.
*/
fn validate_specific_config(&self) -> bool {
fn validate_user_defined_config(&self) -> bool {
let mut result = true;
validate_field!(result, self.config_caching_interval, default_config_caching_interval());
@ -185,15 +187,22 @@ impl ConfigSet {
return Err(ConfigLoadError::InvalidConfigDirectory)
}
// Load default configuration
let default_file = dir_path.join(DEFAULT_CONFIG_FILE_NAME);
let default = Configs::load_config(default_file.as_path())?;
// Load user defined configurations
// TODO: loading with parent merging
let mut specific = Vec::new();
let specific_dir = dir_path.join(USER_CONFIGS_FOLDER_NAME);
if specific_dir.exists() {
// Used to make sure no duplicates are present
let mut name_set = HashSet::new();
let mut name_set = HashSet::new(); // TODO: think about integration with packages
let dir_entry = fs::read_dir(dir_path);
let dir_entry = fs::read_dir(specific_dir);
if dir_entry.is_err() {
return Err(ConfigLoadError::UnableToReadFile)
}
@ -204,11 +213,6 @@ impl ConfigSet {
if let Ok(entry) = entry {
let path = entry.path();
// Skip the default one, already loaded
if path.file_name().unwrap_or("".as_ref()) == "default.yml" {
continue;
}
// Skip non-yaml config files
if path.extension().unwrap_or_default().to_str().unwrap_or_default() != "yml" {
continue;
@ -216,7 +220,7 @@ impl ConfigSet {
let mut config = Configs::load_config(path.as_path())?;
if !config.validate_specific_config() {
if !config.validate_user_defined_config() {
return Err(ConfigLoadError::InvalidParameter(path.to_owned()))
}
@ -251,10 +255,11 @@ impl ConfigSet {
specific.push(config);
}
}
}
Ok(ConfigSet {
default,
specific
specific: specific
})
}
@ -277,6 +282,24 @@ impl ConfigSet {
}
}
// Create auxiliary directories
let user_config_dir = espanso_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() {
return Err(ConfigLoadError::UnableToCreateDefaultConfig)
}
}
let packages_dir = espanso_dir.join(PACKAGES_FOLDER_NAME);
if !packages_dir.exists() {
let res = create_dir_all(packages_dir.as_path());
if res.is_err() {
return Err(ConfigLoadError::UnableToCreateDefaultConfig)
}
}
return ConfigSet::load(espanso_dir.as_path())
}
}
@ -311,8 +334,8 @@ impl fmt::Display for ConfigLoadError {
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"),
ConfigLoadError::InvalidParameter(path) => write!(f, "Invalid parameter in '{}', use of reserved parameters in app-specific configs is not permitted", path.to_str().unwrap_or_default()),
ConfigLoadError::MissingName(path) => write!(f, "The 'name' field is required in app-specific configurations, but it's missing in '{}'", path.to_str().unwrap_or_default()),
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()),
ConfigLoadError::MissingName(path) => write!(f, "The 'name' field is required in user defined configurations, but it's missing in '{}'", path.to_str().unwrap_or_default()),
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"),
}
@ -326,8 +349,8 @@ impl Error for ConfigLoadError {
ConfigLoadError::UnableToReadFile => "Unable to read config file",
ConfigLoadError::InvalidYAML(_, _) => "Error parsing YAML file, invalid syntax",
ConfigLoadError::InvalidConfigDirectory => "Invalid config directory",
ConfigLoadError::InvalidParameter(_) => "Invalid parameter, use of reserved parameters in app-specific configs is not permitted",
ConfigLoadError::MissingName(_) => "The 'name' field is required in app-specific configurations, but it's missing",
ConfigLoadError::InvalidParameter(_) => "Invalid parameter, use of reserved parameters in user defined configs is not permitted",
ConfigLoadError::MissingName(_) => "The 'name' field is required in user defined configurations, but it's missing",
ConfigLoadError::NameDuplicate(_) => "Found duplicate 'name' in some configurations, please use different names",
ConfigLoadError::UnableToCreateDefaultConfig => "Could not generate default config file",
}
@ -397,18 +420,18 @@ mod tests {
}
#[test]
fn test_specific_config_does_not_have_reserved_fields() {
fn test_user_defined_config_does_not_have_reserved_fields() {
let working_config_file = create_tmp_file(r###"
backend: Clipboard
"###);
let config = Configs::load_config(working_config_file.path());
assert_eq!(config.unwrap().validate_specific_config(), true);
assert_eq!(config.unwrap().validate_user_defined_config(), true);
}
#[test]
fn test_specific_config_has_reserved_fields_config_caching_interval() {
fn test_user_defined_config_has_reserved_fields_config_caching_interval() {
let working_config_file = create_tmp_file(r###"
# This should not happen in an app-specific config
@ -416,11 +439,11 @@ mod tests {
"###);
let config = Configs::load_config(working_config_file.path());
assert_eq!(config.unwrap().validate_specific_config(), false);
assert_eq!(config.unwrap().validate_user_defined_config(), false);
}
#[test]
fn test_specific_config_has_reserved_fields_toggle_key() {
fn test_user_defined_config_has_reserved_fields_toggle_key() {
let working_config_file = create_tmp_file(r###"
# This should not happen in an app-specific config
@ -428,11 +451,11 @@ mod tests {
"###);
let config = Configs::load_config(working_config_file.path());
assert_eq!(config.unwrap().validate_specific_config(), false);
assert_eq!(config.unwrap().validate_user_defined_config(), false);
}
#[test]
fn test_specific_config_has_reserved_fields_toggle_interval() {
fn test_user_defined_config_has_reserved_fields_toggle_interval() {
let working_config_file = create_tmp_file(r###"
# This should not happen in an app-specific config
@ -440,11 +463,11 @@ mod tests {
"###);
let config = Configs::load_config(working_config_file.path());
assert_eq!(config.unwrap().validate_specific_config(), false);
assert_eq!(config.unwrap().validate_user_defined_config(), false);
}
#[test]
fn test_specific_config_has_reserved_fields_backspace_limit() {
fn test_user_defined_config_has_reserved_fields_backspace_limit() {
let working_config_file = create_tmp_file(r###"
# This should not happen in an app-specific config
@ -452,7 +475,7 @@ mod tests {
"###);
let config = Configs::load_config(working_config_file.path());
assert_eq!(config.unwrap().validate_specific_config(), false);
assert_eq!(config.unwrap().validate_user_defined_config(), false);
}
#[test]
@ -516,15 +539,14 @@ mod tests {
let default_path = tmp_dir.path().join(DEFAULT_CONFIG_FILE_NAME);
fs::write(default_path, DEFAULT_CONFIG_FILE_CONTENT);
let specific_path = tmp_dir.path().join("specific.yml");
let specific_path_copy = specific_path.clone();
fs::write(specific_path, r###"
let user_defined_path = create_user_config_file(tmp_dir.path(), "specific.yml", r###"
config_caching_interval: 10000
"###);
let user_defined_path_copy = user_defined_path.clone();
let config_set = ConfigSet::load(tmp_dir.path());
assert!(config_set.is_err());
assert_eq!(config_set.unwrap_err(), ConfigLoadError::InvalidParameter(specific_path_copy))
assert_eq!(config_set.unwrap_err(), ConfigLoadError::InvalidParameter(user_defined_path_copy))
}
#[test]
@ -533,15 +555,14 @@ mod tests {
let default_path = tmp_dir.path().join(DEFAULT_CONFIG_FILE_NAME);
fs::write(default_path, DEFAULT_CONFIG_FILE_CONTENT);
let specific_path = tmp_dir.path().join("specific.yml");
let specific_path_copy = specific_path.clone();
fs::write(specific_path, r###"
let user_defined_path = create_user_config_file(tmp_dir.path(), "specific.yml", r###"
backend: Clipboard
"###);
let user_defined_path_copy = user_defined_path.clone();
let config_set = ConfigSet::load(tmp_dir.path());
assert!(config_set.is_err());
assert_eq!(config_set.unwrap_err(), ConfigLoadError::MissingName(specific_path_copy))
assert_eq!(config_set.unwrap_err(), ConfigLoadError::MissingName(user_defined_path_copy))
}
pub fn create_temp_espanso_directory() -> TempDir {
@ -552,23 +573,32 @@ mod tests {
tmp_dir
}
pub fn create_temp_file_in_dir(tmp_dir: &TempDir, name: &str, content: &str) -> PathBuf {
let specific_path = tmp_dir.path().join(name);
let specific_path_copy = specific_path.clone();
fs::write(specific_path, content);
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();
fs::write(user_defined_path, content);
specific_path_copy
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() {
create_dir_all(&user_config_dir);
}
create_temp_file_in_dir(&user_config_dir, name, content)
}
#[test]
fn test_config_set_specific_file_duplicate_name() {
let tmp_dir = create_temp_espanso_directory();
let specific_path = create_temp_file_in_dir(&tmp_dir, "specific.yml", r###"
let user_defined_path = create_user_config_file(tmp_dir.path(), "specific.yml", r###"
name: specific1
"###);
let specific_path2 = create_temp_file_in_dir(&tmp_dir, "specific2.yml", r###"
let user_defined_path2 = create_user_config_file(tmp_dir.path(), "specific2.yml", r###"
name: specific1
"###);
@ -578,7 +608,7 @@ mod tests {
}
#[test]
fn test_specific_config_set_merge_with_parent_matches() {
fn test_user_defined_config_set_merge_with_parent_matches() {
let tmp_dir = TempDir::new().expect("unable to create temp directory");
let default_path = tmp_dir.path().join(DEFAULT_CONFIG_FILE_NAME);
fs::write(default_path, r###"
@ -589,9 +619,7 @@ mod tests {
replace: "Bob"
"###);
let specific_path = tmp_dir.path().join("specific.yml");
let specific_path_copy = specific_path.clone();
fs::write(specific_path, r###"
let user_defined_path = create_user_config_file(tmp_dir.path(), "specific1.yml", r###"
name: specific1
matches:
@ -609,7 +637,7 @@ mod tests {
}
#[test]
fn test_specific_config_set_merge_with_parent_matches_child_priority() {
fn test_user_defined_config_set_merge_with_parent_matches_child_priority() {
let tmp_dir = TempDir::new().expect("unable to create temp directory");
let default_path = tmp_dir.path().join(DEFAULT_CONFIG_FILE_NAME);
fs::write(default_path, r###"
@ -620,9 +648,7 @@ mod tests {
replace: "Bob"
"###);
let specific_path = tmp_dir.path().join("specific.yml");
let specific_path_copy = specific_path.clone();
fs::write(specific_path, r###"
let user_defined_path2 = create_user_config_file(tmp_dir.path(), "specific2.yml", r###"
name: specific1
matches:
@ -639,7 +665,7 @@ mod tests {
}
#[test]
fn test_specific_config_set_exclude_merge_with_parent_matches() {
fn test_user_defined_config_set_exclude_merge_with_parent_matches() {
let tmp_dir = TempDir::new().expect("unable to create temp directory");
let default_path = tmp_dir.path().join(DEFAULT_CONFIG_FILE_NAME);
fs::write(default_path, r###"
@ -650,9 +676,7 @@ mod tests {
replace: "Bob"
"###);
let specific_path = tmp_dir.path().join("specific.yml");
let specific_path_copy = specific_path.clone();
fs::write(specific_path, r###"
let user_defined_path2 = create_user_config_file(tmp_dir.path(), "specific2.yml", r###"
name: specific1
exclude_parent_matches: true
@ -681,9 +705,7 @@ mod tests {
replace: "Bob"
"###);
let specific_path = tmp_dir.path().join("specific.zzz");
let specific_path_copy = specific_path.clone();
fs::write(specific_path, r###"
let user_defined_path2 = create_user_config_file(tmp_dir.path(), "specific.zzz", r###"
name: specific1
exclude_parent_matches: true

View File

@ -210,7 +210,7 @@ mod tests {
use std::fs;
use std::path::PathBuf;
use crate::config::ConfigManager;
use crate::config::tests::{create_temp_espanso_directory, create_temp_file_in_dir};
use crate::config::tests::{create_temp_espanso_directory, create_temp_file_in_dir, create_user_config_file};
struct DummySystemManager {
title: RefCell<String>,
@ -252,18 +252,18 @@ mod tests {
fn test_runtime_constructor_regex_load_correctly() {
let tmp_dir = create_temp_espanso_directory();
let specific_path = create_temp_file_in_dir(&tmp_dir, "specific.yml", r###"
let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###"
name: myname1
filter_exec: "Title"
"###);
let specific_path2 = create_temp_file_in_dir(&tmp_dir, "specific2.yml", r###"
let specific_path2 = create_user_config_file(&tmp_dir.path(), "specific2.yml", r###"
name: myname2
filter_title: "Yeah"
filter_class: "Car"
"###);
let specific_path3 = create_temp_file_in_dir(&tmp_dir, "specific3.yml", r###"
let specific_path3 = create_user_config_file(&tmp_dir.path(), "specific3.yml", r###"
name: myname3
filter_title: "Nice"
"###);
@ -303,18 +303,18 @@ mod tests {
fn test_runtime_constructor_malformed_regexes_are_ignored() {
let tmp_dir = create_temp_espanso_directory();
let specific_path = create_temp_file_in_dir(&tmp_dir, "specific.yml", r###"
let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###"
name: myname1
filter_exec: "[`-_]"
"###);
let specific_path2 = create_temp_file_in_dir(&tmp_dir, "specific2.yml", r###"
let specific_path2 = create_user_config_file(&tmp_dir.path(), "specific2.yml", r###"
name: myname2
filter_title: "[`-_]"
filter_class: "Car"
"###);
let specific_path3 = create_temp_file_in_dir(&tmp_dir, "specific3.yml", r###"
let specific_path3 = create_user_config_file(&tmp_dir.path(), "specific3.yml", r###"
name: myname3
filter_title: "Nice"
"###);
@ -354,7 +354,7 @@ mod tests {
fn test_runtime_calculate_active_config_specific_title_match() {
let tmp_dir = create_temp_espanso_directory();
let specific_path = create_temp_file_in_dir(&tmp_dir, "specific.yml", r###"
let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###"
name: chrome
filter_title: "Chrome"
"###);
@ -372,7 +372,7 @@ mod tests {
fn test_runtime_calculate_active_config_specific_class_match() {
let tmp_dir = create_temp_espanso_directory();
let specific_path = create_temp_file_in_dir(&tmp_dir, "specific.yml", r###"
let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###"
name: chrome
filter_class: "Chrome"
"###);
@ -390,7 +390,7 @@ mod tests {
fn test_runtime_calculate_active_config_specific_exec_match() {
let tmp_dir = create_temp_espanso_directory();
let specific_path = create_temp_file_in_dir(&tmp_dir, "specific.yml", r###"
let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###"
name: chrome
filter_exec: "chrome.exe"
"###);
@ -408,7 +408,7 @@ mod tests {
fn test_runtime_calculate_active_config_specific_multi_filter_match() {
let tmp_dir = create_temp_espanso_directory();
let specific_path = create_temp_file_in_dir(&tmp_dir, "specific.yml", r###"
let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###"
name: chrome
filter_class: Browser
filter_exec: "firefox.exe"
@ -428,7 +428,7 @@ mod tests {
fn test_runtime_calculate_active_config_no_match() {
let tmp_dir = create_temp_espanso_directory();
let specific_path = create_temp_file_in_dir(&tmp_dir, "specific.yml", r###"
let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###"
name: firefox
filter_title: "Firefox"
"###);
@ -447,7 +447,7 @@ mod tests {
fn test_runtime_active_config_cache() {
let tmp_dir = create_temp_espanso_directory();
let specific_path = create_temp_file_in_dir(&tmp_dir, "specific.yml", r###"
let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###"
name: firefox
filter_title: "Firefox"
"###);

View File

@ -51,13 +51,14 @@ mod bridge;
mod engine;
mod config;
mod system;
mod sysdaemon;
mod context;
mod matcher;
mod package;
mod keyboard;
mod protocol;
mod clipboard;
mod extension;
mod sysdaemon;
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const LOG_FILE: &str = "espanso.log";

0
src/package/mod.rs Normal file
View File