From 8dd15bd1c70a07521ecd23d259268fc732b75bd6 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Sun, 11 Feb 2024 04:19:08 +0100 Subject: [PATCH 1/9] feat(env): Make rustic more portable To make rustic more portable, we can use `RUSTIC_HOME` to locate the users wanted config directories without relying on conventional/standard directories varying on the operating system. On Windows we add another layer to get compatibility paths (e.g. in the USERPROFILE). Signed-off-by: simonsan <14062932+simonsan@users.noreply.github.com> --- src/config.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/config.rs b/src/config.rs index 3a6d3757c..de867ccc0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -185,7 +185,17 @@ fn extend(left: &mut HashMap, right: HashMap) { /// /// A vector of [`PathBuf`]s to the config files fn get_config_paths(filename: &str) -> Vec { - [ + let mut paths = vec![]; + + #[cfg(target_os = "windows")] + { + if let Some(win_compatibility_paths) = get_windows_portability_config_directories() { + paths.extend(win_compatibility_paths); + }; + } + + let dirs = vec![ + get_home_config_path(), ProjectDirs::from("", "", "rustic") .map(|project_dirs| project_dirs.config_dir().to_path_buf()), get_global_config_path(), @@ -198,7 +208,46 @@ fn get_config_paths(filename: &str) -> Vec { p }) }) - .collect() + .collect::>(); + + paths.extend(dirs); + paths +} + +/// Get the path to the home config directory. +/// +/// # Returns +/// +/// The path to the home config directory. +/// +/// # Note +/// +/// If the environment variable `RUSTIC_HOME` is not set, `None` is returned. +fn get_home_config_path() -> Option { + std::env::var_os("RUSTIC_HOME").map(|home_dir| { + let mut path = PathBuf::from(home_dir); + path.push(r"config"); + path + }) +} + +/// Get the paths to the user profile config directories on Windows. +/// +/// # Returns +/// +/// A collection of possible paths to the user profile config directory on Windows. +/// +/// # Note +/// +/// If the environment variable `USERPROFILE` is not set, `None` is returned. +#[cfg(target_os = "windows")] +fn get_windows_portability_config_directories() -> Option> { + std::env::var_os("USERPROFILE").map(|path| { + vec![ + PathBuf::from(path.clone()).join(r".config\rustic"), + PathBuf::from(path).join(r".rustic"), + ] + }) } /// Get the path to the global config directory on Windows. From 6a6b42f9daa2c9e6dd5eba91dbcf3e2f211efc5e Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Sun, 11 Feb 2024 04:26:04 +0100 Subject: [PATCH 2/9] fix(config): Use join and don't push to PathBuf Signed-off-by: simonsan <14062932+simonsan@users.noreply.github.com> --- src/config.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/config.rs b/src/config.rs index de867ccc0..86a7e31c6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -224,11 +224,7 @@ fn get_config_paths(filename: &str) -> Vec { /// /// If the environment variable `RUSTIC_HOME` is not set, `None` is returned. fn get_home_config_path() -> Option { - std::env::var_os("RUSTIC_HOME").map(|home_dir| { - let mut path = PathBuf::from(home_dir); - path.push(r"config"); - path - }) + std::env::var_os("RUSTIC_HOME").map(|home_dir| PathBuf::from(home_dir).join("config")) } /// Get the paths to the user profile config directories on Windows. @@ -245,7 +241,7 @@ fn get_windows_portability_config_directories() -> Option> { std::env::var_os("USERPROFILE").map(|path| { vec![ PathBuf::from(path.clone()).join(r".config\rustic"), - PathBuf::from(path).join(r".rustic"), + PathBuf::from(path).join(".rustic"), ] }) } @@ -258,11 +254,8 @@ fn get_windows_portability_config_directories() -> Option> { /// If the environment variable `PROGRAMDATA` is not set, `None` is returned. #[cfg(target_os = "windows")] fn get_global_config_path() -> Option { - std::env::var_os("PROGRAMDATA").map(|program_data| { - let mut path = PathBuf::from(program_data); - path.push(r"rustic\config"); - path - }) + std::env::var_os("PROGRAMDATA") + .map(|program_data| PathBuf::from(program_data).join(r"rustic\config")) } /// Get the path to the global config directory on ios and wasm targets. From fc1a52ead90113ed9997a5ea57abcbd2a831e229 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Sun, 11 Feb 2024 04:50:41 +0100 Subject: [PATCH 3/9] docs(config): rewrite parts of profile section to account for profile locations Signed-off-by: simonsan <14062932+simonsan@users.noreply.github.com> --- config/README.md | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/config/README.md b/config/README.md index 14adcba85..85ee9d422 100644 --- a/config/README.md +++ b/config/README.md @@ -35,12 +35,40 @@ options. Therefore `commandline arguments` have the highest precedence. ## Profiles -Configuration files can be placed in the user's local config directory, e.g. -`~/.config/rustic/` or in the global config dir, e.g. `/etc/rustic/`. You can -use different config files, e.g. `myconfig.toml` and use the `-P` option to -specify the profile name, e.g. `rustic -P myconfig`. Examples for different +To use different configurations for different repositories, you can use profiles. + +For example, you can create a profile called `myconfig` and use it with the `-P` +option, e.g. `rustic -P myconfig`. The configuration file for the profile +`myconfig` should be named `myconfig.toml`. Examples for different configuration files can be found here in the [/config/](/config) directory. +### Profile Locations + +#### \*nix + +Configuration files can be placed in the user's local config directory, e.g. +`~/.config/rustic/` or in the global config dir, e.g. `/etc/rustic/`. + +#### Windows + +On Windows, the configuration file can be placed in the user's local config +directory, e.g. `C:\Users\username\AppData\Roaming\rustic\` or in the global +config dir, e.g. `C:\ProgramData\rustic\config`. The global config directory is +usually not created by the installer, so you may have to create it yourself. +You can also use your User Profile directory, e.g. `C:\Users\username\` and place +the configuration file in the `.rustic` or `.config\rustic` directory. + +#### MacOS + +On MacOS, the configuration file can be placed in the user's local config +directory, e.g. `~/Library/Application Support/rustic/`. + +#### Custom Directory + +If you prefer to use your custom `rustic` directory, you can set `RUSTIC_HOME` +environment variable to point to your custom directory. In it you can place your +configuration file in the `config` subdirectory. + ## Services We have collected some examples how to configure `rustic` for various services From 6f26e0500f3a984beb8d8c2917ed22895d918673 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Sun, 11 Feb 2024 04:51:29 +0100 Subject: [PATCH 4/9] style: fmt Signed-off-by: simonsan <14062932+simonsan@users.noreply.github.com> --- config/README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/config/README.md b/config/README.md index 85ee9d422..61cae3be8 100644 --- a/config/README.md +++ b/config/README.md @@ -35,12 +35,13 @@ options. Therefore `commandline arguments` have the highest precedence. ## Profiles -To use different configurations for different repositories, you can use profiles. +To use different configurations for different repositories, you can use +profiles. For example, you can create a profile called `myconfig` and use it with the `-P` option, e.g. `rustic -P myconfig`. The configuration file for the profile -`myconfig` should be named `myconfig.toml`. Examples for different -configuration files can be found here in the [/config/](/config) directory. +`myconfig` should be named `myconfig.toml`. Examples for different configuration +files can be found here in the [/config/](/config) directory. ### Profile Locations @@ -54,8 +55,8 @@ Configuration files can be placed in the user's local config directory, e.g. On Windows, the configuration file can be placed in the user's local config directory, e.g. `C:\Users\username\AppData\Roaming\rustic\` or in the global config dir, e.g. `C:\ProgramData\rustic\config`. The global config directory is -usually not created by the installer, so you may have to create it yourself. -You can also use your User Profile directory, e.g. `C:\Users\username\` and place +usually not created by the installer, so you may have to create it yourself. You +can also use your User Profile directory, e.g. `C:\Users\username\` and place the configuration file in the `.rustic` or `.config\rustic` directory. #### MacOS From 4e896701713bf162b7a0ba5fe8e225c28086a12e Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Sun, 11 Feb 2024 23:01:35 +0100 Subject: [PATCH 5/9] chore: apply fixes from review Signed-off-by: simonsan <14062932+simonsan@users.noreply.github.com> --- src/config.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/config.rs b/src/config.rs index 86a7e31c6..ad37e97a7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -185,16 +185,7 @@ fn extend(left: &mut HashMap, right: HashMap) { /// /// A vector of [`PathBuf`]s to the config files fn get_config_paths(filename: &str) -> Vec { - let mut paths = vec![]; - - #[cfg(target_os = "windows")] - { - if let Some(win_compatibility_paths) = get_windows_portability_config_directories() { - paths.extend(win_compatibility_paths); - }; - } - - let dirs = vec![ + let mut paths = vec![ get_home_config_path(), ProjectDirs::from("", "", "rustic") .map(|project_dirs| project_dirs.config_dir().to_path_buf()), @@ -210,7 +201,13 @@ fn get_config_paths(filename: &str) -> Vec { }) .collect::>(); - paths.extend(dirs); + #[cfg(target_os = "windows")] + { + if let Some(win_compatibility_paths) = get_windows_portability_config_directories() { + paths.extend(win_compatibility_paths); + }; + } + paths } From 49344993e8c3f38c59ed4202c57ebbc2ed0bbb05 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Sun, 11 Feb 2024 23:10:07 +0100 Subject: [PATCH 6/9] chore: fix clippy lints by allowing due to os dependent impl Signed-off-by: simonsan <14062932+simonsan@users.noreply.github.com> --- src/config.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/config.rs b/src/config.rs index ad37e97a7..773afcc25 100644 --- a/src/config.rs +++ b/src/config.rs @@ -185,6 +185,7 @@ fn extend(left: &mut HashMap, right: HashMap) { /// /// A vector of [`PathBuf`]s to the config files fn get_config_paths(filename: &str) -> Vec { + #[allow(unused_mut)] let mut paths = vec![ get_home_config_path(), ProjectDirs::from("", "", "rustic") @@ -208,6 +209,7 @@ fn get_config_paths(filename: &str) -> Vec { }; } + #[allow(clippy::let_and_return)] paths } From b96173bcf0f52a9e633760f41620c1ec8173d339 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Sun, 11 Feb 2024 23:37:46 +0100 Subject: [PATCH 7/9] refactor: return a collection of optional PathBufs from portability fn to easily extend vec up the call stack Signed-off-by: simonsan <14062932+simonsan@users.noreply.github.com> --- src/config.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/config.rs b/src/config.rs index 773afcc25..ce4487503 100644 --- a/src/config.rs +++ b/src/config.rs @@ -192,15 +192,7 @@ fn get_config_paths(filename: &str) -> Vec { .map(|project_dirs| project_dirs.config_dir().to_path_buf()), get_global_config_path(), Some(PathBuf::from(".")), - ] - .into_iter() - .filter_map(|path| { - path.map(|mut p| { - p.push(filename); - p - }) - }) - .collect::>(); + ]; #[cfg(target_os = "windows")] { @@ -209,8 +201,15 @@ fn get_config_paths(filename: &str) -> Vec { }; } - #[allow(clippy::let_and_return)] paths + .into_iter() + .filter_map(|path| { + path.map(|mut p| { + p.push(filename); + p + }) + }) + .collect::>() } /// Get the path to the home config directory. @@ -236,11 +235,11 @@ fn get_home_config_path() -> Option { /// /// If the environment variable `USERPROFILE` is not set, `None` is returned. #[cfg(target_os = "windows")] -fn get_windows_portability_config_directories() -> Option> { +fn get_windows_portability_config_directories() -> Option>> { std::env::var_os("USERPROFILE").map(|path| { vec![ - PathBuf::from(path.clone()).join(r".config\rustic"), - PathBuf::from(path).join(".rustic"), + Some(PathBuf::from(path.clone()).join(r".config\rustic")), + Some(PathBuf::from(path).join(".rustic")), ] }) } From 6edf7e68a3153bf5429b724134daa80f7ca85897 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Mon, 12 Feb 2024 00:22:48 +0100 Subject: [PATCH 8/9] refactor: replace push with join Signed-off-by: simonsan <14062932+simonsan@users.noreply.github.com> --- src/config.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/config.rs b/src/config.rs index ce4487503..f4d3eabbd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -203,12 +203,7 @@ fn get_config_paths(filename: &str) -> Vec { paths .into_iter() - .filter_map(|path| { - path.map(|mut p| { - p.push(filename); - p - }) - }) + .filter_map(|path| path.map(|p| p.join(filename))) .collect::>() } From bf56c87ffa14494b4dacb770a83d7f498075036f Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Mon, 12 Feb 2024 06:40:18 +0100 Subject: [PATCH 9/9] docs: add comment to explain, why we need to have a lint allowed Signed-off-by: simonsan <14062932+simonsan@users.noreply.github.com> --- src/config.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/config.rs b/src/config.rs index f4d3eabbd..8e3892926 100644 --- a/src/config.rs +++ b/src/config.rs @@ -185,6 +185,9 @@ fn extend(left: &mut HashMap, right: HashMap) { /// /// A vector of [`PathBuf`]s to the config files fn get_config_paths(filename: &str) -> Vec { + // we need this mut here, because we want to add data to the Vec + // depending on the OS. Our CI is running on Unix, and doesn't have knowledge of + // us mutating that vector #[allow(unused_mut)] let mut paths = vec![ get_home_config_path(),