Add config_set tests
This commit is contained in:
parent
6c5d69372b
commit
20fbb622a1
98
Cargo.lock
generated
98
Cargo.lock
generated
|
@ -114,6 +114,15 @@ dependencies = [
|
|||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "c2-chacha"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.41"
|
||||
|
@ -224,6 +233,7 @@ dependencies = [
|
|||
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"simplelog 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zip 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -263,6 +273,16 @@ name = "fuchsia-cprng"
|
|||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
|
@ -326,6 +346,11 @@ name = "podio"
|
|||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "0.4.30"
|
||||
|
@ -358,6 +383,27 @@ dependencies = [
|
|||
"proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.3.1"
|
||||
|
@ -371,6 +417,22 @@ name = "rand_core"
|
|||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_os"
|
||||
version = "0.1.3"
|
||||
|
@ -424,6 +486,14 @@ name = "regex-syntax"
|
|||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-argon2"
|
||||
version = "0.5.1"
|
||||
|
@ -514,6 +584,19 @@ dependencies = [
|
|||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.6.1"
|
||||
|
@ -569,6 +652,11 @@ name = "vec_map"
|
|||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "0.4.0"
|
||||
|
@ -629,6 +717,7 @@ dependencies = [
|
|||
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||
"checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b"
|
||||
"checksum bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6584aa36f5ad4c9247f5323b0a42f37802b37a836f0ad87084d7a33961abe25f"
|
||||
"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101"
|
||||
"checksum cc 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "8dae9c4b8fedcae85592ba623c4fd08cfdab3e3b72d6df780c6ead964a69bfff"
|
||||
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||
"checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68"
|
||||
|
@ -645,6 +734,7 @@ dependencies = [
|
|||
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
|
||||
"checksum flate2 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "2adaffba6388640136149e18ed080b77a78611c1e1d6de75aedcdf78df5d4682"
|
||||
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571"
|
||||
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
|
||||
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
|
||||
|
@ -655,18 +745,24 @@ dependencies = [
|
|||
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
|
||||
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
|
||||
"checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd"
|
||||
"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b"
|
||||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||
"checksum proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "175a40b9cf564ce9bf050654633dbf339978706b8ead1a907bb970b63185dd95"
|
||||
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
||||
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||
"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c"
|
||||
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
|
||||
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
||||
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||
"checksum redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d"
|
||||
"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd"
|
||||
"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
|
||||
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
|
||||
"checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf"
|
||||
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
|
||||
"checksum serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "fec2851eb56d010dc9a21b89ca53ee75e6528bab60c11e89d38390904982da9f"
|
||||
|
@ -677,6 +773,7 @@ dependencies = [
|
|||
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||
"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
|
||||
"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f"
|
||||
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
"checksum term 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5"
|
||||
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||
|
@ -685,6 +782,7 @@ dependencies = [
|
|||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||
"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
|
||||
"checksum widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effc0e4ff8085673ea7b9b2e3c73f6bd4d118810c9009ed8f1e16bd96c331db6"
|
||||
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
|
|
@ -16,5 +16,8 @@ log = "0.4.8"
|
|||
simplelog = "0.7.1"
|
||||
zip = "0.5.3"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
cmake = "0.1.31"
|
|
@ -1,7 +1,7 @@
|
|||
extern crate dirs;
|
||||
|
||||
use std::path::Path;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{fs, io};
|
||||
use crate::matcher::Match;
|
||||
use std::fs::{File, create_dir_all};
|
||||
use std::io::Read;
|
||||
|
@ -11,6 +11,8 @@ use crate::system::SystemManager;
|
|||
use std::collections::HashSet;
|
||||
use std::process::exit;
|
||||
use log::{debug, info, warn, error};
|
||||
use std::fmt;
|
||||
use std::error::Error;
|
||||
|
||||
pub(crate) mod runtime;
|
||||
|
||||
|
@ -71,8 +73,10 @@ pub struct Configs {
|
|||
macro_rules! validate_field {
|
||||
($result:expr, $field:expr, $def_value:expr) => {
|
||||
if $field != $def_value {
|
||||
let field_name = stringify!($field);
|
||||
let field_name = &field_name[5..]; // Remove the 'self.' prefix
|
||||
let mut field_name = stringify!($field);
|
||||
if field_name.starts_with("self.") {
|
||||
field_name = &field_name[5..]; // Remove the 'self.' prefix
|
||||
}
|
||||
error!("Validation error, parameter '{}' is reserved and can be only used in the default.yaml config file", field_name);
|
||||
$result = false;
|
||||
}
|
||||
|
@ -85,7 +89,7 @@ impl Configs {
|
|||
* It makes sure that app-specific config instances do not define
|
||||
* attributes reserved to the default config.
|
||||
*/
|
||||
fn validate_config(&self) -> bool {
|
||||
fn validate_specific_config(&self) -> bool {
|
||||
let mut result = true;
|
||||
|
||||
validate_field!(result, self.config_caching_interval, default_config_caching_interval());
|
||||
|
@ -109,24 +113,26 @@ impl Default for BackendType {
|
|||
}
|
||||
|
||||
impl Configs {
|
||||
fn load_config(path: &Path) -> Configs {
|
||||
fn load_config(path: &Path) -> Result<Configs, ConfigLoadError> {
|
||||
let file_res = File::open(path);
|
||||
if let Ok(mut file) = file_res {
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)
|
||||
.expect("Unable to read config file");
|
||||
let res = file.read_to_string(&mut contents);
|
||||
|
||||
if let Err(_) = res {
|
||||
return Err(ConfigLoadError::UnableToReadFile)
|
||||
}
|
||||
|
||||
let config_res = serde_yaml::from_str(&contents);
|
||||
|
||||
match config_res {
|
||||
Ok(config) => config,
|
||||
Ok(config) => Ok(config),
|
||||
Err(e) => {
|
||||
error!("Error parsing YAML file {}, invalid syntax: {}", path.to_str().unwrap_or(""), e);
|
||||
exit(2);
|
||||
Err(ConfigLoadError::InvalidYAML(path.to_owned(), e.to_string()))
|
||||
}
|
||||
}
|
||||
}else{
|
||||
panic!("Config file not found...")
|
||||
Err(ConfigLoadError::FileNotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,62 +143,62 @@ pub struct ConfigSet {
|
|||
specific: Vec<Configs>,
|
||||
}
|
||||
|
||||
impl ConfigSet { // TODO: tests
|
||||
pub fn load(dir_path: &Path) -> ConfigSet {
|
||||
if !dir_path.is_dir() {
|
||||
error!("Invalid config directory");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
let default_file = dir_path.join(DEFAULT_CONFIG_FILE_NAME);
|
||||
let default = Configs::load_config(default_file.as_path());
|
||||
|
||||
let mut specific = Vec::new();
|
||||
|
||||
// Used to make sure no duplicates are present
|
||||
let mut name_set = HashSet::new();
|
||||
|
||||
for entry in fs::read_dir(dir_path)
|
||||
.expect("Cannot read espanso config directory!") {
|
||||
|
||||
let entry = entry;
|
||||
if let Ok(entry) = entry {
|
||||
let path = entry.path();
|
||||
|
||||
// Skip the default one, already loaded
|
||||
if path.file_name().unwrap_or("".as_ref()) == "default.yaml" {
|
||||
continue;
|
||||
}
|
||||
|
||||
let config = Configs::load_config(path.as_path());
|
||||
|
||||
if !config.validate_config() {
|
||||
error!("Error while parsing {}, please remove reserved parameters", path.to_str().unwrap_or(""));
|
||||
exit(3);
|
||||
}
|
||||
|
||||
if config.name == "default" {
|
||||
error!("Error while parsing {}, please specify a 'name' field", path.to_str().unwrap_or(""));
|
||||
exit(4);
|
||||
}
|
||||
|
||||
if name_set.contains(&config.name) {
|
||||
error!("Error while parsing {} : the specified name is already used, please use another one", path.to_str().unwrap_or(""));
|
||||
exit(5);
|
||||
}
|
||||
|
||||
name_set.insert(config.name.clone());
|
||||
specific.push(config);
|
||||
impl ConfigSet {
|
||||
pub fn load(dir_path: &Path) -> Result<ConfigSet, ConfigLoadError> {
|
||||
if !dir_path.is_dir() {
|
||||
return Err(ConfigLoadError::InvalidConfigDirectory)
|
||||
}
|
||||
|
||||
let default_file = dir_path.join(DEFAULT_CONFIG_FILE_NAME);
|
||||
let default = Configs::load_config(default_file.as_path())?;
|
||||
|
||||
let mut specific = Vec::new();
|
||||
|
||||
// Used to make sure no duplicates are present
|
||||
let mut name_set = HashSet::new();
|
||||
|
||||
let dir_entry = fs::read_dir(dir_path);
|
||||
if dir_entry.is_err() {
|
||||
return Err(ConfigLoadError::UnableToReadFile)
|
||||
}
|
||||
let dir_entry = dir_entry.unwrap();
|
||||
|
||||
for entry in dir_entry {
|
||||
let entry = entry;
|
||||
if let Ok(entry) = entry {
|
||||
let path = entry.path();
|
||||
|
||||
// Skip the default one, already loaded
|
||||
if path.file_name().unwrap_or("".as_ref()) == "default.yaml" {
|
||||
continue;
|
||||
}
|
||||
|
||||
let config = Configs::load_config(path.as_path())?;
|
||||
|
||||
if !config.validate_specific_config() {
|
||||
return Err(ConfigLoadError::InvalidParameter(path.to_owned()))
|
||||
}
|
||||
|
||||
if config.name == "default" {
|
||||
return Err(ConfigLoadError::MissingName(path.to_owned()));
|
||||
}
|
||||
|
||||
if name_set.contains(&config.name) {
|
||||
return Err(ConfigLoadError::NameDuplicate(path.to_owned()));
|
||||
}
|
||||
|
||||
name_set.insert(config.name.clone());
|
||||
specific.push(config);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ConfigSet {
|
||||
default,
|
||||
specific
|
||||
})
|
||||
}
|
||||
|
||||
ConfigSet {
|
||||
default,
|
||||
specific
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_default() -> ConfigSet {
|
||||
pub fn load_default() -> Result<ConfigSet, ConfigLoadError> {
|
||||
let res = dirs::home_dir();
|
||||
if let Some(home_dir) = res {
|
||||
let espanso_dir = home_dir.join(".espanso");
|
||||
|
@ -205,16 +211,17 @@ pub fn load(dir_path: &Path) -> ConfigSet {
|
|||
|
||||
// If config file does not exist, create one from template
|
||||
if !default_file.exists() {
|
||||
fs::write(&default_file, DEFAULT_CONFIG_FILE_CONTENT)
|
||||
.expect("Unable to write default config file");
|
||||
let result = fs::write(&default_file, DEFAULT_CONFIG_FILE_CONTENT);
|
||||
if result.is_err() {
|
||||
return Err(ConfigLoadError::UnableToCreateDefaultConfig)
|
||||
}
|
||||
}
|
||||
|
||||
return ConfigSet::load(espanso_dir.as_path())
|
||||
}
|
||||
}
|
||||
|
||||
error!("Could not generate default position for config file");
|
||||
exit(1);
|
||||
return Err(ConfigLoadError::UnableToCreateDefaultConfig)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,4 +229,276 @@ 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>;
|
||||
}
|
||||
|
||||
// Error handling
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ConfigLoadError {
|
||||
FileNotFound,
|
||||
UnableToReadFile,
|
||||
InvalidYAML(PathBuf, String),
|
||||
InvalidConfigDirectory,
|
||||
InvalidParameter(PathBuf),
|
||||
MissingName(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"),
|
||||
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::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",
|
||||
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::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::*;
|
||||
use std::io::Write;
|
||||
use tempfile::{NamedTempFile, TempDir};
|
||||
|
||||
const TEST_WORKING_CONFIG_FILE : &str = include_str!("../res/test/working_config.yaml");
|
||||
const TEST_CONFIG_FILE_WITH_BAD_YAML : &str = include_str!("../res/test/config_with_bad_yaml.yaml");
|
||||
|
||||
// Test Configs
|
||||
|
||||
fn create_tmp_file(string: &str) -> NamedTempFile {
|
||||
let file = NamedTempFile::new().unwrap();
|
||||
file.as_file().write_all(string.as_bytes());
|
||||
file
|
||||
}
|
||||
|
||||
#[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 {
|
||||
Ok(_) => {assert!(false)},
|
||||
Err(e) => {
|
||||
match e {
|
||||
ConfigLoadError::InvalidYAML(p, _) => assert_eq!(p, broken_config_file.path().to_owned()),
|
||||
_ => assert!(false),
|
||||
}
|
||||
assert!(true);
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn test_specific_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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_specific_config_has_reserved_fields_config_caching_interval() {
|
||||
let working_config_file = create_tmp_file(r###"
|
||||
|
||||
# This should not happen in an app-specific config
|
||||
config_caching_interval: 100
|
||||
|
||||
"###);
|
||||
let config = Configs::load_config(working_config_file.path());
|
||||
assert_eq!(config.unwrap().validate_specific_config(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_specific_config_has_reserved_fields_toggle_key() {
|
||||
let working_config_file = create_tmp_file(r###"
|
||||
|
||||
# This should not happen in an app-specific config
|
||||
toggle_key: CTRL
|
||||
|
||||
"###);
|
||||
let config = Configs::load_config(working_config_file.path());
|
||||
assert_eq!(config.unwrap().validate_specific_config(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_specific_config_has_reserved_fields_toggle_interval() {
|
||||
let working_config_file = create_tmp_file(r###"
|
||||
|
||||
# This should not happen in an app-specific config
|
||||
toggle_interval: 1000
|
||||
|
||||
"###);
|
||||
let config = Configs::load_config(working_config_file.path());
|
||||
assert_eq!(config.unwrap().validate_specific_config(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_specific_config_has_reserved_fields_backspace_limit() {
|
||||
let working_config_file = create_tmp_file(r###"
|
||||
|
||||
# This should not happen in an app-specific config
|
||||
backspace_limit: 10
|
||||
|
||||
"###);
|
||||
let config = Configs::load_config(working_config_file.path());
|
||||
assert_eq!(config.unwrap().validate_specific_config(), false);
|
||||
}
|
||||
|
||||
#[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
|
||||
|
||||
#[test]
|
||||
fn test_config_set_default_content_should_work_correctly() {
|
||||
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, DEFAULT_CONFIG_FILE_CONTENT);
|
||||
|
||||
let config_set = ConfigSet::load(tmp_dir.path());
|
||||
assert!(config_set.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_load_fail_bad_directory() {
|
||||
let config_set = ConfigSet::load(Path::new("invalid/path"));
|
||||
assert_eq!(config_set.is_err(), true);
|
||||
assert_eq!(config_set.unwrap_err(), ConfigLoadError::InvalidConfigDirectory);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_missing_default_file() {
|
||||
let tmp_dir = TempDir::new().expect("unable to create temp directory");
|
||||
|
||||
let config_set = ConfigSet::load(tmp_dir.path());
|
||||
assert_eq!(config_set.is_err(), true);
|
||||
assert_eq!(config_set.unwrap_err(), ConfigLoadError::FileNotFound);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_invalid_yaml_syntax() {
|
||||
let tmp_dir = TempDir::new().expect("unable to create temp directory");
|
||||
let default_path = tmp_dir.path().join(DEFAULT_CONFIG_FILE_NAME);
|
||||
let default_path_copy = default_path.clone();
|
||||
fs::write(default_path, TEST_CONFIG_FILE_WITH_BAD_YAML);
|
||||
|
||||
let config_set = ConfigSet::load(tmp_dir.path());
|
||||
match config_set {
|
||||
Ok(_) => {assert!(false)},
|
||||
Err(e) => {
|
||||
match e {
|
||||
ConfigLoadError::InvalidYAML(p, _) => assert_eq!(p, default_path_copy),
|
||||
_ => assert!(false),
|
||||
}
|
||||
assert!(true);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_specific_file_with_reserved_fields() {
|
||||
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, DEFAULT_CONFIG_FILE_CONTENT);
|
||||
|
||||
let specific_path = tmp_dir.path().join("specific.yaml");
|
||||
let specific_path_copy = specific_path.clone();
|
||||
fs::write(specific_path, r###"
|
||||
config_caching_interval: 10000
|
||||
"###);
|
||||
|
||||
let config_set = ConfigSet::load(tmp_dir.path());
|
||||
assert!(config_set.is_err());
|
||||
assert_eq!(config_set.unwrap_err(), ConfigLoadError::InvalidParameter(specific_path_copy))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_specific_file_missing_name() {
|
||||
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, DEFAULT_CONFIG_FILE_CONTENT);
|
||||
|
||||
let specific_path = tmp_dir.path().join("specific.yaml");
|
||||
let specific_path_copy = specific_path.clone();
|
||||
fs::write(specific_path, r###"
|
||||
backend: Clipboard
|
||||
"###);
|
||||
|
||||
let config_set = ConfigSet::load(tmp_dir.path());
|
||||
assert!(config_set.is_err());
|
||||
assert_eq!(config_set.unwrap_err(), ConfigLoadError::MissingName(specific_path_copy))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_specific_file_duplicate_name() {
|
||||
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, DEFAULT_CONFIG_FILE_CONTENT);
|
||||
|
||||
let specific_path = tmp_dir.path().join("specific.yaml");
|
||||
let specific_path_copy = specific_path.clone();
|
||||
fs::write(specific_path, r###"
|
||||
name: specific1
|
||||
"###);
|
||||
|
||||
let specific_path2 = tmp_dir.path().join("specific2.yaml");
|
||||
let specific_path_copy2 = specific_path2.clone();
|
||||
fs::write(specific_path2, r###"
|
||||
name: specific1
|
||||
"###);
|
||||
|
||||
let config_set = ConfigSet::load(tmp_dir.path());
|
||||
assert!(config_set.is_err());
|
||||
assert_eq!(config_set.unwrap_err(), ConfigLoadError::NameDuplicate(specific_path_copy2))
|
||||
}
|
||||
}
|
|
@ -11,8 +11,9 @@ use std::{thread, time};
|
|||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use std::sync::mpsc::Receiver;
|
||||
use log::{info, LevelFilter};
|
||||
use log::{info, error, LevelFilter};
|
||||
use simplelog::{CombinedLogger, TermLogger, TerminalMode};
|
||||
use std::process::exit;
|
||||
|
||||
mod ui;
|
||||
mod bridge;
|
||||
|
@ -70,7 +71,10 @@ fn main() {
|
|||
info!("loading configuration from custom location: {}", path);
|
||||
ConfigSet::load(Path::new(path))
|
||||
},
|
||||
};
|
||||
}.unwrap_or_else(|e| {
|
||||
error!("{}", e);
|
||||
exit(1);
|
||||
});
|
||||
|
||||
if matches.is_present("dump") {
|
||||
println!("{:#?}", config_set);
|
||||
|
|
12
src/res/test/config_with_bad_yaml.yaml
Normal file
12
src/res/test/config_with_bad_yaml.yaml
Normal file
|
@ -0,0 +1,12 @@
|
|||
backend: Clipboard
|
||||
|
||||
definitely a bad yaml
|
||||
|
||||
matches:
|
||||
# Default
|
||||
- trigger: ":espanso"
|
||||
replace: "Hi there!"
|
||||
|
||||
# Emojis
|
||||
- trigger: ":lol"
|
||||
replace: "😂"
|
10
src/res/test/working_config.yaml
Normal file
10
src/res/test/working_config.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
backend: Clipboard
|
||||
|
||||
matches:
|
||||
# Default
|
||||
- trigger: ":espanso"
|
||||
replace: "Hi there!"
|
||||
|
||||
# Emojis
|
||||
- trigger: ":lol"
|
||||
replace: "😂"
|
Loading…
Reference in New Issue
Block a user