From cfbb15ac4839228bce262acd2f30da4d68a3b02e Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Wed, 9 Oct 2019 00:37:42 +0200 Subject: [PATCH] Moved default location for configuration and packages. Fix #73. Merge #77. Create path subcommand. Updated tests --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/config/mod.rs | 352 +++++++++++++++++++---------------------- src/config/runtime.rs | 58 +++---- src/context/mod.rs | 56 ++++++- src/main.rs | 34 ++-- src/package/default.rs | 2 +- 7 files changed, 258 insertions(+), 248 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e16150..979c3ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,7 +329,7 @@ dependencies = [ [[package]] name = "espanso" -version = "0.2.4" +version = "0.3.0" dependencies = [ "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index caa07c1..58f45d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "espanso" -version = "0.2.4" +version = "0.3.0" authors = ["Federico Terzi "] license = "GPL-3.0" description = "Cross-platform Text Expander written in Rust" diff --git a/src/config/mod.rs b/src/config/mod.rs index 6838a39..ffb5307 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -38,7 +38,6 @@ 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() } @@ -212,26 +211,25 @@ pub struct ConfigSet { } impl ConfigSet { - pub fn load(dir_path: &Path) -> Result { - if !dir_path.is_dir() { + pub fn load(config_dir: &Path, package_dir: &Path) -> Result { + if !config_dir.is_dir() { return Err(ConfigLoadError::InvalidConfigDirectory) } // Load default configuration - let default_file = dir_path.join(DEFAULT_CONFIG_FILE_NAME); + let default_file = config_dir.join(DEFAULT_CONFIG_FILE_NAME); let default = Configs::load_config(default_file.as_path())?; // Analyze which config files has to be loaded let mut target_files = Vec::new(); - let specific_dir = dir_path.join(USER_CONFIGS_FOLDER_NAME); + let specific_dir = config_dir.join(USER_CONFIGS_FOLDER_NAME); if specific_dir.exists() { let dir_entry = WalkDir::new(specific_dir); target_files.extend(dir_entry); } - let package_dir = dir_path.join(PACKAGES_FOLDER_NAME); if package_dir.exists() { let dir_entry = WalkDir::new(package_dir); target_files.extend(dir_entry); @@ -320,54 +318,40 @@ impl ConfigSet { } pub fn load_default() -> Result { - let config_dir = ConfigSet::get_default_config_dir(); + // Configuration related - // Create the espanso dir if it doesn't exist - let res = create_dir_all(config_dir.as_path()); + let config_dir = crate::context::get_config_dir(); - if res.is_ok() { - let default_file = config_dir.join(DEFAULT_CONFIG_FILE_NAME); + let default_file = config_dir.join(DEFAULT_CONFIG_FILE_NAME); - // 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() { - return Err(ConfigLoadError::UnableToCreateDefaultConfig) - } + // 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() { + return Err(ConfigLoadError::UnableToCreateDefaultConfig) } - - // Create auxiliary directories - - 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() { - return Err(ConfigLoadError::UnableToCreateDefaultConfig) - } - } - - let packages_dir = ConfigSet::get_default_packages_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(config_dir.as_path()) } - Err(ConfigLoadError::UnableToCreateDefaultConfig) - } + // Create auxiliary directories - pub fn get_default_config_dir() -> PathBuf { - let config_dir = dirs::config_dir().expect("Unable to get config directory"); - config_dir.join("espanso") - } + 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() { + return Err(ConfigLoadError::UnableToCreateDefaultConfig) + } + } - pub fn get_default_packages_dir() -> PathBuf { - let data_dir = ConfigSet::get_default_config_dir(); - data_dir.join(PACKAGES_FOLDER_NAME) + + // Packages + + let package_dir = crate::context::get_package_dir(); + let res = create_dir_all(package_dir.as_path()); + if res.is_err() { + return Err(ConfigLoadError::UnableToCreateDefaultConfig) // TODO: change error type + } + + return ConfigSet::load(config_dir.as_path(), package_dir.as_path()); } } @@ -547,94 +531,18 @@ mod tests { // 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()); + pub fn create_temp_espanso_directories() -> (TempDir, TempDir) { + create_temp_espanso_directories_with_default_content(DEFAULT_CONFIG_FILE_CONTENT) } - #[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); - } + pub fn create_temp_espanso_directories_with_default_content(default_content: &str) -> (TempDir, TempDir) { + let data_dir = TempDir::new().expect("unable to create data directory"); + let package_dir = TempDir::new().expect("unable to create package directory"); - #[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 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(user_defined_path_copy)) - } - - #[test] - fn test_config_set_specific_file_missing_name_auto_generated() { - 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 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_ok()); - assert_eq!(config_set.unwrap().specific[0].name, user_defined_path_copy.to_str().unwrap_or_default()) - } - - pub fn create_temp_espanso_directory() -> TempDir { - create_temp_espanso_directory_with_default_content(DEFAULT_CONFIG_FILE_CONTENT) - } - - pub fn create_temp_espanso_directory_with_default_content(default_content: &str) -> TempDir { - 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 = data_dir.path().join(DEFAULT_CONFIG_FILE_NAME); fs::write(default_path, default_content); - tmp_dir + (data_dir, package_dir) } pub fn create_temp_file_in_dir(tmp_dir: &PathBuf, name: &str, content: &str) -> PathBuf { @@ -654,9 +562,8 @@ mod tests { create_temp_file_in_dir(&user_config_dir, name, content) } - pub fn create_package_file(tmp_dir: &Path, package_name: &str, filename: &str, content: &str) -> PathBuf { - let package_config_dir = tmp_dir.join(PACKAGES_FOLDER_NAME); - let package_dir = package_config_dir.join(package_name); + pub fn create_package_file(package_data_dir: &Path, package_name: &str, filename: &str, content: &str) -> PathBuf { + let package_dir = package_data_dir.join(package_name); if !package_dir.exists() { create_dir_all(&package_dir); } @@ -664,28 +571,99 @@ mod tests { create_temp_file_in_dir(&package_dir, filename, content) } + #[test] + fn test_config_set_default_content_should_work_correctly() { + let (data_dir, package_dir) = create_temp_espanso_directories(); + + let config_set = ConfigSet::load(data_dir.path(), package_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"), 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 data_dir = TempDir::new().expect("unable to create temp directory"); + let package_dir = TempDir::new().expect("unable to create package directory"); + + let config_set = ConfigSet::load(data_dir.path(), package_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 (data_dir, package_dir) = create_temp_espanso_directories_with_default_content( + TEST_CONFIG_FILE_WITH_BAD_YAML + ); + let default_path = data_dir.path().join(DEFAULT_CONFIG_FILE_NAME); + + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); + match config_set { + Ok(_) => {assert!(false)}, + Err(e) => { + match e { + ConfigLoadError::InvalidYAML(p, _) => assert_eq!(p, default_path), + _ => assert!(false), + } + assert!(true); + }, + } + } + + #[test] + fn test_config_set_specific_file_with_reserved_fields() { + let (data_dir, package_dir) = create_temp_espanso_directories(); + + let user_defined_path = create_user_config_file(data_dir.path(), "specific.yml", r###" + config_caching_interval: 10000 + "###); + let user_defined_path_copy = user_defined_path.clone(); + + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); + assert!(config_set.is_err()); + assert_eq!(config_set.unwrap_err(), ConfigLoadError::InvalidParameter(user_defined_path_copy)) + } + + #[test] + fn test_config_set_specific_file_missing_name_auto_generated() { + let (data_dir, package_dir) = create_temp_espanso_directories(); + + let user_defined_path = create_user_config_file(data_dir.path(), "specific.yml", r###" + backend: Clipboard + "###); + let user_defined_path_copy = user_defined_path.clone(); + + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); + assert!(config_set.is_ok()); + assert_eq!(config_set.unwrap().specific[0].name, user_defined_path_copy.to_str().unwrap_or_default()) + } + #[test] fn test_config_set_specific_file_duplicate_name() { - let tmp_dir = create_temp_espanso_directory(); + let (data_dir, package_dir) = create_temp_espanso_directories(); - let user_defined_path = create_user_config_file(tmp_dir.path(), "specific.yml", r###" + let user_defined_path = create_user_config_file(data_dir.path(), "specific.yml", r###" name: specific1 "###); - let user_defined_path2 = create_user_config_file(tmp_dir.path(), "specific2.yml", r###" + let user_defined_path2 = create_user_config_file(data_dir.path(), "specific2.yml", r###" name: specific1 "###); - let config_set = ConfigSet::load(tmp_dir.path()); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); assert!(config_set.is_err()); assert!(variant_eq(&config_set.unwrap_err(), &ConfigLoadError::NameDuplicate(PathBuf::new()))) } #[test] 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###" + let (data_dir, package_dir) = create_temp_espanso_directories_with_default_content(r###" matches: - trigger: ":lol" replace: "LOL" @@ -693,7 +671,7 @@ mod tests { replace: "Bob" "###); - let user_defined_path = create_user_config_file(tmp_dir.path(), "specific1.yml", r###" + let user_defined_path = create_user_config_file(data_dir.path(), "specific1.yml", r###" name: specific1 matches: @@ -701,7 +679,7 @@ mod tests { replace: "newstring" "###); - let config_set = ConfigSet::load(tmp_dir.path()).unwrap(); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.default.matches.len(), 2); assert_eq!(config_set.specific[0].matches.len(), 3); @@ -712,9 +690,7 @@ mod tests { #[test] 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###" + let (data_dir, package_dir) = create_temp_espanso_directories_with_default_content(r###" matches: - trigger: ":lol" replace: "LOL" @@ -722,7 +698,7 @@ mod tests { replace: "Bob" "###); - let user_defined_path2 = create_user_config_file(tmp_dir.path(), "specific2.yml", r###" + let user_defined_path2 = create_user_config_file(data_dir.path(), "specific2.yml", r###" name: specific1 matches: @@ -730,7 +706,7 @@ mod tests { replace: "newstring" "###); - let config_set = ConfigSet::load(tmp_dir.path()).unwrap(); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.default.matches.len(), 2); assert_eq!(config_set.specific[0].matches.len(), 2); @@ -740,9 +716,7 @@ mod tests { #[test] 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###" + let (data_dir, package_dir) = create_temp_espanso_directories_with_default_content(r###" matches: - trigger: ":lol" replace: "LOL" @@ -750,7 +724,7 @@ mod tests { replace: "Bob" "###); - let user_defined_path2 = create_user_config_file(tmp_dir.path(), "specific2.yml", r###" + let user_defined_path2 = create_user_config_file(data_dir.path(), "specific2.yml", r###" name: specific1 exclude_default_matches: true @@ -760,7 +734,7 @@ mod tests { replace: "newstring" "###); - let config_set = ConfigSet::load(tmp_dir.path()).unwrap(); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.default.matches.len(), 2); assert_eq!(config_set.specific[0].matches.len(), 1); @@ -769,17 +743,17 @@ mod tests { #[test] fn test_only_yaml_files_are_loaded_from_config() { - 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###" - matches: - - trigger: ":lol" - replace: "LOL" - - trigger: ":yess" - replace: "Bob" - "###); + let (data_dir, package_dir) = create_temp_espanso_directories_with_default_content( + r###" + matches: + - trigger: ":lol" + replace: "LOL" + - trigger: ":yess" + replace: "Bob" + "### + ); - let user_defined_path2 = create_user_config_file(tmp_dir.path(), "specific.zzz", r###" + let user_defined_path2 = create_user_config_file(data_dir.path(), "specific.zzz", r###" name: specific1 exclude_default_matches: true @@ -789,35 +763,35 @@ mod tests { replace: "newstring" "###); - let config_set = ConfigSet::load(tmp_dir.path()).unwrap(); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 0); } #[test] fn test_config_set_no_parent_configs_works_correctly() { - let tmp_dir = create_temp_espanso_directory(); + let (data_dir, package_dir) = create_temp_espanso_directories(); - let user_defined_path = create_user_config_file(tmp_dir.path(), "specific.yml", r###" + let user_defined_path = create_user_config_file(data_dir.path(), "specific.yml", r###" name: specific1 "###); - let user_defined_path2 = create_user_config_file(tmp_dir.path(), "specific2.yml", r###" + let user_defined_path2 = create_user_config_file(data_dir.path(), "specific2.yml", r###" name: specific2 "###); - let config_set = ConfigSet::load(tmp_dir.path()).unwrap(); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 2); } #[test] fn test_config_set_default_parent_works_correctly() { - let tmp_dir = create_temp_espanso_directory_with_default_content(r###" + let (data_dir, package_dir) = create_temp_espanso_directories_with_default_content(r###" matches: - trigger: hasta replace: Hasta la vista "###); - let user_defined_path = create_user_config_file(tmp_dir.path(), "specific.yml", r###" + let user_defined_path = create_user_config_file(data_dir.path(), "specific.yml", r###" parent: default matches: @@ -825,7 +799,7 @@ mod tests { replace: "world" "###); - let config_set = ConfigSet::load(tmp_dir.path()).unwrap(); + 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(), 2); assert!(config_set.default.matches.iter().any(|m| m.trigger == "hasta")); @@ -834,19 +808,19 @@ mod tests { #[test] fn test_config_set_no_parent_should_not_merge() { - let tmp_dir = create_temp_espanso_directory_with_default_content(r###" + let (data_dir, package_dir)= create_temp_espanso_directories_with_default_content(r###" matches: - trigger: hasta replace: Hasta la vista "###); - let user_defined_path = create_user_config_file(tmp_dir.path(), "specific.yml", r###" + let user_defined_path = create_user_config_file(data_dir.path(), "specific.yml", r###" matches: - trigger: "hello" replace: "world" "###); - let config_set = ConfigSet::load(tmp_dir.path()).unwrap(); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 1); assert_eq!(config_set.default.matches.len(), 1); assert!(config_set.default.matches.iter().any(|m| m.trigger == "hasta")); @@ -856,13 +830,13 @@ mod tests { #[test] fn test_config_set_default_nested_parent_works_correctly() { - let tmp_dir = create_temp_espanso_directory_with_default_content(r###" + let (data_dir, package_dir) = create_temp_espanso_directories_with_default_content(r###" matches: - trigger: hasta replace: Hasta la vista "###); - let user_defined_path = create_user_config_file(tmp_dir.path(), "specific.yml", r###" + let user_defined_path = create_user_config_file(data_dir.path(), "specific.yml", r###" name: custom1 parent: default @@ -871,7 +845,7 @@ mod tests { replace: "world" "###); - let user_defined_path2 = create_user_config_file(tmp_dir.path(), "specific2.yml", r###" + let user_defined_path2 = create_user_config_file(data_dir.path(), "specific2.yml", r###" parent: custom1 matches: @@ -879,7 +853,7 @@ mod tests { replace: "mario" "###); - let config_set = ConfigSet::load(tmp_dir.path()).unwrap(); + 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(), 3); assert!(config_set.default.matches.iter().any(|m| m.trigger == "hasta")); @@ -889,13 +863,13 @@ mod tests { #[test] fn test_config_set_parent_merge_children_priority_should_be_higher() { - let tmp_dir = create_temp_espanso_directory_with_default_content(r###" + let (data_dir, package_dir) = create_temp_espanso_directories_with_default_content(r###" matches: - trigger: hasta replace: Hasta la vista "###); - let user_defined_path = create_user_config_file(tmp_dir.path(), "specific.yml", r###" + let user_defined_path = create_user_config_file(data_dir.path(), "specific.yml", r###" parent: default matches: @@ -903,7 +877,7 @@ mod tests { replace: "world" "###); - let config_set = ConfigSet::load(tmp_dir.path()).unwrap(); + 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); assert!(config_set.default.matches.iter().any(|m| m.trigger == "hasta" && m.replace == "world")); @@ -911,13 +885,13 @@ mod tests { #[test] fn test_config_set_package_configs_default_merge() { - let tmp_dir = create_temp_espanso_directory_with_default_content(r###" + let (data_dir, package_dir) = create_temp_espanso_directories_with_default_content(r###" matches: - trigger: hasta replace: Hasta la vista "###); - let package_path = create_package_file(tmp_dir.path(), "package1", "package.yml", r###" + let package_path = create_package_file(package_dir.path(), "package1", "package.yml", r###" parent: default matches: @@ -925,7 +899,7 @@ mod tests { replace: "potter" "###); - let config_set = ConfigSet::load(tmp_dir.path()).unwrap(); + 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(), 2); assert!(config_set.default.matches.iter().any(|m| m.trigger == "hasta")); @@ -934,19 +908,19 @@ mod tests { #[test] fn test_config_set_package_configs_without_merge() { - let tmp_dir = create_temp_espanso_directory_with_default_content(r###" + let (data_dir, package_dir) = create_temp_espanso_directories_with_default_content(r###" matches: - trigger: hasta replace: Hasta la vista "###); - let package_path = create_package_file(tmp_dir.path(), "package1", "package.yml", r###" + let package_path = create_package_file(package_dir.path(), "package1", "package.yml", r###" matches: - trigger: "harry" replace: "potter" "###); - let config_set = ConfigSet::load(tmp_dir.path()).unwrap(); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 1); assert_eq!(config_set.default.matches.len(), 1); assert!(config_set.default.matches.iter().any(|m| m.trigger == "hasta")); @@ -955,13 +929,13 @@ mod tests { #[test] fn test_config_set_package_configs_multiple_files() { - let tmp_dir = create_temp_espanso_directory_with_default_content(r###" + let (data_dir, package_dir) = create_temp_espanso_directories_with_default_content(r###" matches: - trigger: hasta replace: Hasta la vista "###); - let package_path = create_package_file(tmp_dir.path(), "package1", "package.yml", r###" + let package_path = create_package_file(package_dir.path(), "package1", "package.yml", r###" name: package1 matches: @@ -969,7 +943,7 @@ mod tests { replace: "potter" "###); - let package_path2 = create_package_file(tmp_dir.path(), "package1", "addon.yml", r###" + let package_path2 = create_package_file(package_dir.path(), "package1", "addon.yml", r###" parent: package1 matches: @@ -977,7 +951,7 @@ mod tests { replace: "weasley" "###); - let config_set = ConfigSet::load(tmp_dir.path()).unwrap(); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 1); assert_eq!(config_set.default.matches.len(), 1); assert!(config_set.default.matches.iter().any(|m| m.trigger == "hasta")); diff --git a/src/config/runtime.rs b/src/config/runtime.rs index 3bb0a8b..2e2aff0 100644 --- a/src/config/runtime.rs +++ b/src/config/runtime.rs @@ -209,7 +209,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, create_user_config_file}; + use crate::config::tests::{create_temp_espanso_directories, create_temp_file_in_dir, create_user_config_file}; struct DummySystemManager { title: RefCell, @@ -249,25 +249,25 @@ mod tests { #[test] fn test_runtime_constructor_regex_load_correctly() { - let tmp_dir = create_temp_espanso_directory(); + let (data_dir, package_dir) = create_temp_espanso_directories(); - let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###" + let specific_path = create_user_config_file(&data_dir.path(), "specific.yml", r###" name: myname1 filter_exec: "Title" "###); - let specific_path2 = create_user_config_file(&tmp_dir.path(), "specific2.yml", r###" + let specific_path2 = create_user_config_file(&data_dir.path(), "specific2.yml", r###" name: myname2 filter_title: "Yeah" filter_class: "Car" "###); - let specific_path3 = create_user_config_file(&tmp_dir.path(), "specific3.yml", r###" + let specific_path3 = create_user_config_file(&data_dir.path(), "specific3.yml", r###" name: myname3 filter_title: "Nice" "###); - let config_set = ConfigSet::load(tmp_dir.path()); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); assert!(config_set.is_ok()); let dummy_system_manager = DummySystemManager::new(); @@ -300,25 +300,25 @@ mod tests { #[test] fn test_runtime_constructor_malformed_regexes_are_ignored() { - let tmp_dir = create_temp_espanso_directory(); + let (data_dir, package_dir) = create_temp_espanso_directories(); - let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###" + let specific_path = create_user_config_file(&data_dir.path(), "specific.yml", r###" name: myname1 filter_exec: "[`-_]" "###); - let specific_path2 = create_user_config_file(&tmp_dir.path(), "specific2.yml", r###" + let specific_path2 = create_user_config_file(&data_dir.path(), "specific2.yml", r###" name: myname2 filter_title: "[`-_]" filter_class: "Car" "###); - let specific_path3 = create_user_config_file(&tmp_dir.path(), "specific3.yml", r###" + let specific_path3 = create_user_config_file(&data_dir.path(), "specific3.yml", r###" name: myname3 filter_title: "Nice" "###); - let config_set = ConfigSet::load(tmp_dir.path()); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); assert!(config_set.is_ok()); let dummy_system_manager = DummySystemManager::new(); @@ -351,14 +351,14 @@ mod tests { #[test] fn test_runtime_calculate_active_config_specific_title_match() { - let tmp_dir = create_temp_espanso_directory(); + let (data_dir, package_dir) = create_temp_espanso_directories(); - let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###" + let specific_path = create_user_config_file(&data_dir.path(), "specific.yml", r###" name: chrome filter_title: "Chrome" "###); - let config_set = ConfigSet::load(tmp_dir.path()); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); assert!(config_set.is_ok()); let dummy_system_manager = DummySystemManager::new_custom("Google Chrome", "Chrome", "C:\\Path\\chrome.exe"); @@ -369,14 +369,14 @@ mod tests { } fn test_runtime_calculate_active_config_specific_class_match() { - let tmp_dir = create_temp_espanso_directory(); + let (data_dir, package_dir) = create_temp_espanso_directories(); - let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###" + let specific_path = create_user_config_file(&data_dir.path(), "specific.yml", r###" name: chrome filter_class: "Chrome" "###); - let config_set = ConfigSet::load(tmp_dir.path()); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); assert!(config_set.is_ok()); let dummy_system_manager = DummySystemManager::new_custom("Google Chrome", "Chrome", "C:\\Path\\chrome.exe"); @@ -387,14 +387,14 @@ mod tests { } fn test_runtime_calculate_active_config_specific_exec_match() { - let tmp_dir = create_temp_espanso_directory(); + let (data_dir, package_dir) = create_temp_espanso_directories(); - let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###" + let specific_path = create_user_config_file(&data_dir.path(), "specific.yml", r###" name: chrome filter_exec: "chrome.exe" "###); - let config_set = ConfigSet::load(tmp_dir.path()); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); assert!(config_set.is_ok()); let dummy_system_manager = DummySystemManager::new_custom("Google Chrome", "Chrome", "C:\\Path\\chrome.exe"); @@ -405,15 +405,15 @@ mod tests { } fn test_runtime_calculate_active_config_specific_multi_filter_match() { - let tmp_dir = create_temp_espanso_directory(); + let (data_dir, package_dir) = create_temp_espanso_directories(); - let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###" + let specific_path = create_user_config_file(&data_dir.path(), "specific.yml", r###" name: chrome filter_class: Browser filter_exec: "firefox.exe" "###); - let config_set = ConfigSet::load(tmp_dir.path()); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); assert!(config_set.is_ok()); let dummy_system_manager = DummySystemManager::new_custom("Google Chrome", "Browser", "C:\\Path\\chrome.exe"); @@ -425,14 +425,14 @@ mod tests { #[test] fn test_runtime_calculate_active_config_no_match() { - let tmp_dir = create_temp_espanso_directory(); + let (data_dir, package_dir) = create_temp_espanso_directories(); - let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###" + let specific_path = create_user_config_file(&data_dir.path(), "specific.yml", r###" name: firefox filter_title: "Firefox" "###); - let config_set = ConfigSet::load(tmp_dir.path()); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); assert!(config_set.is_ok()); let dummy_system_manager = DummySystemManager::new_custom("Google Chrome", "Chrome", "C:\\Path\\chrome.exe"); @@ -444,14 +444,14 @@ mod tests { #[test] fn test_runtime_active_config_cache() { - let tmp_dir = create_temp_espanso_directory(); + let (data_dir, package_dir) = create_temp_espanso_directories(); - let specific_path = create_user_config_file(&tmp_dir.path(), "specific.yml", r###" + let specific_path = create_user_config_file(&data_dir.path(), "specific.yml", r###" name: firefox filter_title: "Firefox" "###); - let config_set = ConfigSet::load(tmp_dir.path()); + let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); assert!(config_set.is_ok()); let dummy_system_manager = DummySystemManager::new_custom("Google Chrome", "Chrome", "C:\\Path\\chrome.exe"); diff --git a/src/context/mod.rs b/src/context/mod.rs index 6827c69..fdce4c8 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -35,13 +35,6 @@ pub trait Context { fn eventloop(&self); } -pub fn get_data_dir() -> PathBuf { - let data_dir = dirs::data_local_dir().expect("Can't obtain data_dir(), terminating."); - let espanso_dir = data_dir.join("espanso"); - create_dir_all(&espanso_dir).expect("Error creating espanso data directory"); - espanso_dir -} - // MAC IMPLEMENTATION #[cfg(target_os = "macos")] pub fn new(send_channel: Sender) -> Box { @@ -58,4 +51,53 @@ pub fn new(send_channel: Sender) -> Box { #[cfg(target_os = "windows")] pub fn new(send_channel: Sender) -> Box { windows::WindowsContext::new(send_channel) +} + +// espanso directories + +pub fn get_data_dir() -> PathBuf { + let data_dir = dirs::data_local_dir().expect("Can't obtain data_local_dir(), terminating."); + let espanso_dir = data_dir.join("espanso"); + create_dir_all(&espanso_dir).expect("Error creating espanso data directory"); + espanso_dir +} + +pub fn get_config_dir() -> PathBuf { + // For compatibility purposes, check if the $HOME/.espanso directory is available + let home_dir = dirs::home_dir().expect("Can't obtain the user home directory, terminating."); + let legacy_espanso_dir = home_dir.join(".espanso"); + if legacy_espanso_dir.exists() { + eprintln!("WARNING: using legacy espanso config location in $HOME/.espanso is DEPRECATED"); + eprintln!("Starting from espanso v0.3.0, espanso config location is changed."); + eprintln!("Please check out the documentation to find out more: https://espanso.org/docs/configuration/"); + + return legacy_espanso_dir; + } + + // New config location, from version v0.3.0 + // Refer to issue #73 for more information: https://github.com/federico-terzi/espanso/issues/73 + let config_dir = dirs::config_dir().expect("Can't obtain config_dir(), terminating."); + let espanso_dir = config_dir.join("espanso"); + create_dir_all(&espanso_dir).expect("Error creating espanso config directory"); + espanso_dir +} + +const PACKAGES_FOLDER_NAME : &str = "packages"; + +pub fn get_package_dir() -> PathBuf { + // Deprecated $HOME/.espanso/packages directory compatibility check + let home_dir = dirs::home_dir().expect("Can't obtain the user home directory, terminating."); + let legacy_espanso_dir = home_dir.join(".espanso"); + if legacy_espanso_dir.exists() { + let legacy_package_dir = legacy_espanso_dir.join(PACKAGES_FOLDER_NAME); + if legacy_package_dir.exists() { + return legacy_package_dir; + } + } + + // New package location, starting from version v0.3.0 + let data_dir = get_data_dir(); + let package_dir = data_dir.join(PACKAGES_FOLDER_NAME); + create_dir_all(&package_dir).expect("Error creating espanso packages directory"); + package_dir } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 0620c70..e7ea97f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -81,12 +81,6 @@ fn main() { .version(VERSION) .author("Federico Terzi") .about("Cross-platform Text Expander written in Rust") - .arg(Arg::with_name("config") - .short("c") - .long("config") - .value_name("FILE") - .help("Sets a custom config directory. If not specified, reads the default $HOME/.espanso/default.yml file, creating it if not present.") - .takes_value(true)) .arg(Arg::with_name("v") .short("v") .multiple(true) @@ -122,6 +116,8 @@ fn main() { .about("Restart the espanso daemon.")) .subcommand(SubCommand::with_name("status") .about("Check if the espanso daemon is running or not.")) + .subcommand(SubCommand::with_name("path") + .about("Prints all the current espanso directory paths, to easily locate configuration and data paths.")) // Package manager .subcommand(SubCommand::with_name("package") @@ -145,20 +141,7 @@ fn main() { let log_level = matches.occurrences_of("v") as i32; // Load the configuration - let mut config_set = match matches.value_of("config") { - None => { - if log_level > 1 { - println!("loading configuration from default location..."); - } - ConfigSet::load_default() - }, - Some(path) => { - if log_level > 1 { - println!("loading configuration from custom location: {}", path); - } - ConfigSet::load(Path::new(path)) - }, - }.unwrap_or_else(|e| { + let mut config_set = ConfigSet::load_default().unwrap_or_else(|e| { println!("{}", e); exit(1); }); @@ -232,6 +215,11 @@ fn main() { return; } + if matches.subcommand_matches("path").is_some() { + path_main(config_set); + return; + } + if let Some(matches) = matches.subcommand_matches("package") { if let Some(matches) = matches.subcommand_matches("install") { install_main(config_set, matches); @@ -744,6 +732,12 @@ fn list_package_main(_config_set: ConfigSet, matches: &ArgMatches) { } } +fn path_main(_config_set: ConfigSet) { + println!("Config: {}", crate::context::get_config_dir().to_string_lossy()); + println!("Packages: {}", crate::context::get_package_dir().to_string_lossy()); + println!("Data: {}", crate::context::get_config_dir().to_string_lossy()); +} + fn acquire_lock() -> Option { let espanso_dir = context::get_data_dir(); diff --git a/src/package/default.rs b/src/package/default.rs index 8950d59..dfbff4a 100644 --- a/src/package/default.rs +++ b/src/package/default.rs @@ -54,7 +54,7 @@ impl DefaultPackageManager { pub fn new_default() -> DefaultPackageManager { DefaultPackageManager::new( - crate::config::ConfigSet::get_default_packages_dir(), + crate::context::get_package_dir(), crate::context::get_data_dir() ) }