|
92 | 92 | //! [`basic_toml` crate]: https://docs.rs/basic_toml
|
93 | 93 |
|
94 | 94 | mod utils;
|
| 95 | +use etcetera::app_strategy; |
95 | 96 | use utils::*;
|
96 | 97 |
|
97 |
| -use etcetera::{AppStrategy, AppStrategyArgs, app_strategy::choose_app_strategy}; |
| 98 | +use etcetera::{ |
| 99 | + AppStrategy, AppStrategyArgs, app_strategy::choose_app_strategy, |
| 100 | + app_strategy::choose_native_strategy, |
| 101 | +}; |
| 102 | +use lazy_static::lazy_static; |
98 | 103 | use serde::{Serialize, de::DeserializeOwned};
|
99 | 104 | use std::fs::{self, File, OpenOptions, Permissions};
|
100 | 105 | use std::io::{ErrorKind::NotFound, Write};
|
101 | 106 | use std::path::{Path, PathBuf};
|
| 107 | +use std::sync::Mutex; |
102 | 108 | use thiserror::Error;
|
103 | 109 |
|
104 | 110 | #[cfg(feature = "toml_conf")]
|
@@ -153,6 +159,10 @@ const EXTENSION: &str = "yml";
|
153 | 159 | #[cfg(feature = "ron_conf")]
|
154 | 160 | const EXTENSION: &str = "ron";
|
155 | 161 |
|
| 162 | +lazy_static! { |
| 163 | + static ref STRATEGY: Mutex<ConfigStrategy> = Mutex::new(ConfigStrategy::App); |
| 164 | +} |
| 165 | + |
156 | 166 | /// The errors the confy crate can encounter.
|
157 | 167 | #[derive(Debug, Error)]
|
158 | 168 | pub enum ConfyError {
|
@@ -202,6 +212,84 @@ pub enum ConfyError {
|
202 | 212 | SetPermissionsFileError(#[source] std::io::Error),
|
203 | 213 | }
|
204 | 214 |
|
| 215 | +/// Determine what strategy `confy` should use |
| 216 | +pub enum ConfigStrategy { |
| 217 | + App, |
| 218 | + Native, |
| 219 | +} |
| 220 | + |
| 221 | +/// Change the strategy to use |
| 222 | +/// |
| 223 | +/// default is the AppStrategy |
| 224 | +pub fn change_config_strategy(changer: ConfigStrategy) { |
| 225 | + *STRATEGY |
| 226 | + .lock() |
| 227 | + .expect("Error getting lock on Config Stragey") = changer; |
| 228 | +} |
| 229 | + |
| 230 | +enum InternalStrategy { |
| 231 | + App(app_strategy::Xdg), |
| 232 | + NativeMac(app_strategy::Apple), |
| 233 | + NativeUnix(app_strategy::Unix), |
| 234 | + NativeWindows(app_strategy::Windows), |
| 235 | +} |
| 236 | + |
| 237 | +// we only every access the config dir function |
| 238 | +impl AppStrategy for InternalStrategy { |
| 239 | + fn home_dir(&self) -> &Path { |
| 240 | + unimplemented!() |
| 241 | + } |
| 242 | + |
| 243 | + fn config_dir(&self) -> PathBuf { |
| 244 | + match self { |
| 245 | + InternalStrategy::App(xdg) => xdg.config_dir(), |
| 246 | + InternalStrategy::NativeMac(mac) => mac.config_dir(), |
| 247 | + InternalStrategy::NativeUnix(unix) => unix.config_dir(), |
| 248 | + InternalStrategy::NativeWindows(windows) => windows.config_dir(), |
| 249 | + } |
| 250 | + } |
| 251 | + |
| 252 | + fn data_dir(&self) -> PathBuf { |
| 253 | + unimplemented!() |
| 254 | + } |
| 255 | + |
| 256 | + fn cache_dir(&self) -> PathBuf { |
| 257 | + unimplemented!() |
| 258 | + } |
| 259 | + |
| 260 | + fn state_dir(&self) -> Option<PathBuf> { |
| 261 | + unimplemented!() |
| 262 | + } |
| 263 | + |
| 264 | + fn runtime_dir(&self) -> Option<PathBuf> { |
| 265 | + unimplemented!() |
| 266 | + } |
| 267 | +} |
| 268 | + |
| 269 | +impl From<app_strategy::Xdg> for InternalStrategy { |
| 270 | + fn from(value: app_strategy::Xdg) -> Self { |
| 271 | + InternalStrategy::App(value) |
| 272 | + } |
| 273 | +} |
| 274 | + |
| 275 | +impl From<app_strategy::Apple> for InternalStrategy { |
| 276 | + fn from(value: app_strategy::Apple) -> Self { |
| 277 | + InternalStrategy::NativeMac(value) |
| 278 | + } |
| 279 | +} |
| 280 | + |
| 281 | +impl From<app_strategy::Unix> for InternalStrategy { |
| 282 | + fn from(value: app_strategy::Unix) -> Self { |
| 283 | + InternalStrategy::NativeUnix(value) |
| 284 | + } |
| 285 | +} |
| 286 | + |
| 287 | +impl From<app_strategy::Windows> for InternalStrategy { |
| 288 | + fn from(value: app_strategy::Windows) -> Self { |
| 289 | + InternalStrategy::NativeWindows(value) |
| 290 | + } |
| 291 | +} |
| 292 | + |
205 | 293 | /// Load an application configuration from disk
|
206 | 294 | ///
|
207 | 295 | /// A new configuration file is created with default values if none
|
@@ -469,14 +557,29 @@ pub fn get_configuration_file_path<'a>(
|
469 | 557 | config_name: impl Into<Option<&'a str>>,
|
470 | 558 | ) -> Result<PathBuf, ConfyError> {
|
471 | 559 | let config_name = config_name.into().unwrap_or("default-config");
|
472 |
| - let project = choose_app_strategy(AppStrategyArgs { |
473 |
| - top_level_domain: "rs".to_string(), |
474 |
| - author: "".to_string(), |
475 |
| - app_name: app_name.to_string(), |
476 |
| - }) |
477 |
| - .map_err(|e| { |
478 |
| - ConfyError::BadConfigDirectory(format!("could not determine home directory path: {e}")) |
479 |
| - })?; |
| 560 | + let project: InternalStrategy = match *STRATEGY |
| 561 | + .lock() |
| 562 | + .expect("Error getting lock on config strategy") |
| 563 | + { |
| 564 | + ConfigStrategy::App => choose_app_strategy(AppStrategyArgs { |
| 565 | + top_level_domain: "rs".to_string(), |
| 566 | + author: "".to_string(), |
| 567 | + app_name: app_name.to_string(), |
| 568 | + }) |
| 569 | + .map_err(|e| { |
| 570 | + ConfyError::BadConfigDirectory(format!("could not determine home directory path: {e}")) |
| 571 | + })? |
| 572 | + .into(), |
| 573 | + ConfigStrategy::Native => choose_native_strategy(AppStrategyArgs { |
| 574 | + top_level_domain: "rs".to_string(), |
| 575 | + author: "".to_string(), |
| 576 | + app_name: app_name.to_string(), |
| 577 | + }) |
| 578 | + .map_err(|e| { |
| 579 | + ConfyError::BadConfigDirectory(format!("could not determine home directory path: {e}")) |
| 580 | + })? |
| 581 | + .into(), |
| 582 | + }; |
480 | 583 |
|
481 | 584 | let config_dir_str = get_configuration_directory_str(&project)?;
|
482 | 585 |
|
@@ -572,6 +675,44 @@ mod tests {
|
572 | 675 | })
|
573 | 676 | }
|
574 | 677 |
|
| 678 | + #[test] |
| 679 | + fn test_store_path_native() { |
| 680 | + // change the strategy first then the app will always use it |
| 681 | + change_config_strategy(ConfigStrategy::Native); |
| 682 | + |
| 683 | + with_config_path(|path| { |
| 684 | + let config: ExampleConfig = ExampleConfig { |
| 685 | + name: "Test".to_string(), |
| 686 | + count: 42, |
| 687 | + }; |
| 688 | + |
| 689 | + let file_path = get_configuration_file_path("example-app", "example-config").unwrap(); |
| 690 | + |
| 691 | + if cfg!(target_os = "macos") { |
| 692 | + assert_eq!( |
| 693 | + file_path, |
| 694 | + Path::new("/Users/rth/Library/Preferences/rs.example-app/example-config.toml"), |
| 695 | + ); |
| 696 | + } else if cfg!(target_os = "linux") { |
| 697 | + assert_eq!( |
| 698 | + file_path, |
| 699 | + Path::new(".config/example-app/example-config.toml") |
| 700 | + ); |
| 701 | + } else { |
| 702 | + //windows |
| 703 | + assert_eq!( |
| 704 | + file_path, |
| 705 | + Path::new("AppData/Roaming/example-app/example-config/config"), |
| 706 | + ); |
| 707 | + } |
| 708 | + |
| 709 | + // Make sure it is still the same config file |
| 710 | + store_path(path, &config).expect("store_path failed"); |
| 711 | + let loaded = load_path(path).expect("load_path failed"); |
| 712 | + assert_eq!(config, loaded); |
| 713 | + }) |
| 714 | + } |
| 715 | + |
575 | 716 | /// [`store_path_perms`] stores [`ExampleConfig`], with only read permission for owner (UNIX).
|
576 | 717 | #[test]
|
577 | 718 | #[cfg(unix)]
|
|
0 commit comments