diff --git a/Cargo.toml b/Cargo.toml index f65e4f5..630ce18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,10 @@ [package] categories = ["config"] -description = "An unopinionated library for obtaining configuration, data and cache directories" +description = "An unopinionated library for obtaining configuration, data, cache, & other directories" documentation = "https://docs.rs/etcetera" edition = "2018" homepage = "https://github.com/lunacookies/etcetera" +keywords = ["xdg", "dirs", "directories", "directory", "basedir", "app_dirs", "cli_dirs", "config", "path", "folder"] license = "MIT OR Apache-2.0" name = "etcetera" readme = "README.md" @@ -13,4 +14,3 @@ version = "0.5.0" [dependencies] cfg-if = "1.0.0" home = "0.5.4" -thiserror = "1.0.22" diff --git a/README.md b/README.md index 1140a42..94baccc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,36 @@ +[![crates.io version](https://img.shields.io/crates/v/etcetera?style=for-the-badge)](https://crates.io/crates/etcetera) +[![crates.io revdeps](https://img.shields.io/crates/d/etcetera?style=for-the-badge)](https://crates.io/crates/etcetera/reverse_dependencies) +[![documentation](https://img.shields.io/docsrs/etcetera?style=for-the-badge)](https://docs.rs/etcetera) +![license](https://img.shields.io/crates/l/etcetera?style=for-the-badge) + # Etcetera -This is a Rust library that aims to allow you to determine the locations of configuration, cache and data files for your application. Existing Rust libraries generally do not give you a choice in terms of which standards/conventions (Etcetera calls these ‘strategies’) they follow. Etcetera, on the other hand, gives you the choice. +This is a Rust library that allows you to determine the locations of configuration, data, cache & other files for your application. Existing Rust libraries generally do not give you a choice in terms of which standards/conventions (Etcetera calls these ‘strategies’) they follow. Etcetera, on the other hand, gives you the choice. + +Etcetera supports the following strategies: +- the [XDG base directory](https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html) +- the [Known Folder Locations](https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457.aspx) +- the [Standard Directories](https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html) +- the "Unix Single-folder Strategy" i.e. everything in `~/.myapp` + +Etcetera has 2 modes of operation: `BaseStrategy` & `AppStrategy`: +- With `BaseStrategy`, you just get the location of the respective directory. For eg. for `config_dir()`: + - XDG: `~/.config` + - Apple: `~/Library/Preferences` + - Windows: `~\AppData\Roaming` +- With `AppStrategy`, you provide additional information to get the location of your app directory. + For eg. if you provide the following details: `{ top_level_domain: "org", author: "Acme Corp", app_name: "Frobnicator Plus" }`, you'll get: + - XDG: `~/.config/frobnicator-plus` + - Unix: `~/.frobnicator-plus` + - Apple: `~/Library/Preferences/org.acmecorp.FrobnicatorPlus` + - Windows: `~\AppData\Roaming\Acme Corp\Frobnicator Plus` + +Note: the location of the home (~) is determined by the [`home`](https://docs.rs/home/0.5.4/home/fn.home_dir.html) crate. + +Etcetera also provides convenience functions for selecting the appropriate strategy on each platform: +- `base_strategy::choose_base_strategy` & `app_strategy::choose_app_strategy`: Uses `Windows` on Windows & `XDG` everywhere else. + This is used by most CLI tools & some GUI tools on each platform. +- `base_strategy::choose_native_strategy` & `app_strategy::choose_native_strategy`: Uses `Windows` on Windows, `Apple` on macOS/iOS, & `XDG` everywhere else. + This is used by most GUI applications on each platform. -See the documentation for more. +See the documentation for examples. diff --git a/src/app_strategy.rs b/src/app_strategy.rs index 6f88bb3..272c6bd 100644 --- a/src/app_strategy.rs +++ b/src/app_strategy.rs @@ -22,12 +22,12 @@ impl AppStrategyArgs { /// use etcetera::app_strategy::AppStrategyArgs; /// /// let strategy_args = AppStrategyArgs { - /// top_level_domain: "com".to_string(), - /// author: "Apple".to_string(), - /// app_name: "Safari".to_string(), + /// top_level_domain: "org".to_string(), + /// author: "Acme Corp".to_string(), + /// app_name: "Frobnicator Plus".to_string(), /// }; /// - /// assert_eq!(strategy_args.bundle_id(), "com.apple.Safari".to_string()); + /// assert_eq!(strategy_args.bundle_id().replace(' ', ""), "org.acmecorp.FrobnicatorPlus".to_string()); /// ``` pub fn bundle_id(&self) -> String { format!( @@ -45,11 +45,11 @@ impl AppStrategyArgs { /// /// let strategy_args = AppStrategyArgs { /// top_level_domain: "org".to_string(), - /// author: "Mozilla".to_string(), - /// app_name: "Firefox Developer Edition".to_string(), + /// author: "Acme Corp".to_string(), + /// app_name: "Frobnicator Plus".to_string(), /// }; /// - /// assert_eq!(strategy_args.unixy_name(), "firefox-developer-edition".to_string()); + /// assert_eq!(strategy_args.unixy_name(), "frobnicator-plus".to_string()); /// ``` pub fn unixy_name(&self) -> String { self.app_name.to_lowercase().replace(' ', "-") @@ -60,9 +60,13 @@ macro_rules! in_dir_method { ($self: ident, $path_extra: expr, $dir_method_name: ident) => {{ let mut path = $self.$dir_method_name(); path.push(Path::new(&$path_extra)); - path }}; + (opt: $self: ident, $path_extra: expr, $dir_method_name: ident) => {{ + let mut path = $self.$dir_method_name()?; + path.push(Path::new(&$path_extra)); + Some(path) + }}; } /// Allows applications to retrieve the paths of configuration, data and cache directories specifically for them. @@ -104,6 +108,16 @@ pub trait AppStrategy: Sized { fn in_cache_dir>(&self, path: P) -> PathBuf { in_dir_method!(self, path, cache_dir) } + + /// Constructs a path inside your application’s state directory to which a path of your choice has been appended. + fn in_state_dir>(&self, path: P) -> Option { + in_dir_method!(opt: self, path, state_dir) + } + + /// Constructs a path inside your application’s runtime directory to which a path of your choice has been appended. + fn in_runtime_dir>(&self, path: P) -> Option { + in_dir_method!(opt: self, path, runtime_dir) + } } macro_rules! create_strategies { diff --git a/src/app_strategy/apple.rs b/src/app_strategy/apple.rs index 31f9dce..2b39ac3 100644 --- a/src/app_strategy/apple.rs +++ b/src/app_strategy/apple.rs @@ -11,25 +11,25 @@ use std::path::PathBuf; /// use std::path::Path; /// /// let app_strategy = Apple::new(AppStrategyArgs { -/// top_level_domain: "com".to_string(), -/// author: "Apple".to_string(), -/// app_name: "Safari".to_string(), +/// top_level_domain: "org".to_string(), +/// author: "Acme Corp".to_string(), +/// app_name: "Frobnicator Plus".to_string(), /// }).unwrap(); /// /// let home_dir = etcetera::home_dir().unwrap(); /// /// assert_eq!( /// app_strategy.config_dir().strip_prefix(&home_dir), -/// Ok(Path::new("Library/Preferences/com.apple.Safari/") -/// )); +/// Ok(Path::new("Library/Preferences/org.acmecorp.FrobnicatorPlus/")) +/// ); /// assert_eq!( /// app_strategy.data_dir().strip_prefix(&home_dir), -/// Ok(Path::new("Library/Application Support/com.apple.Safari/") -/// )); +/// Ok(Path::new("Library/Application Support/org.acmecorp.FrobnicatorPlus/")) +/// ); /// assert_eq!( /// app_strategy.cache_dir().strip_prefix(&home_dir), -/// Ok(Path::new("Library/Caches/com.apple.Safari/") -/// )); +/// Ok(Path::new("Library/Caches/org.acmecorp.FrobnicatorPlus/")) +/// ); /// assert_eq!( /// app_strategy.state_dir(), /// None @@ -51,7 +51,7 @@ impl super::AppStrategy for Apple { fn new(args: super::AppStrategyArgs) -> Result { Ok(Self { base_strategy: base_strategy::Apple::new()?, - bundle_id: args.bundle_id(), + bundle_id: args.bundle_id().replace(' ', ""), }) } diff --git a/src/app_strategy/unix.rs b/src/app_strategy/unix.rs index 1c4c53e..6a29c06 100644 --- a/src/app_strategy/unix.rs +++ b/src/app_strategy/unix.rs @@ -10,36 +10,36 @@ use std::path::PathBuf; /// /// let app_strategy = Unix::new(AppStrategyArgs { /// top_level_domain: "org".to_string(), -/// author: "Bram Moolenar".to_string(), -/// app_name: "Vim".to_string(), +/// author: "Acme Corp".to_string(), +/// app_name: "Frobnicator Plus".to_string(), /// }).unwrap(); /// /// let home_dir = etcetera::home_dir().unwrap(); /// /// assert_eq!( /// app_strategy.config_dir().strip_prefix(&home_dir), -/// Ok(Path::new(".vim/") -/// )); +/// Ok(Path::new(".frobnicator-plus/")) +/// ); /// assert_eq!( /// app_strategy.data_dir().strip_prefix(&home_dir), -/// Ok(Path::new(".vim/data/") -/// )); +/// Ok(Path::new(".frobnicator-plus/data/")) +/// ); /// assert_eq!( /// app_strategy.cache_dir().strip_prefix(&home_dir), -/// Ok(Path::new(".vim/cache/") -/// )); +/// Ok(Path::new(".frobnicator-plus/cache/")) +/// ); /// assert_eq!( /// app_strategy.state_dir().unwrap().strip_prefix(&home_dir), -/// Ok(Path::new(".vim/state/") -/// )); +/// Ok(Path::new(".frobnicator-plus/state/")) +/// ); /// assert_eq!( /// app_strategy.runtime_dir().unwrap().strip_prefix(&home_dir), -/// Ok(Path::new(".vim/runtime/") -/// )); +/// Ok(Path::new(".frobnicator-plus/runtime/")) +/// ); /// ``` #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Unix { - // This is `.vim` in the above example. + // This is `.frobnicator-plus` in the above example. root_dir: PathBuf, } diff --git a/src/app_strategy/windows.rs b/src/app_strategy/windows.rs index 50d1744..0cec286 100644 --- a/src/app_strategy/windows.rs +++ b/src/app_strategy/windows.rs @@ -11,24 +11,24 @@ use std::path::PathBuf; /// use std::path::Path; /// /// let app_strategy = Windows::new(AppStrategyArgs { -/// top_level_domain: "com".to_string(), -/// author: "Microsoft".to_string(), -/// app_name: "File Explorer".to_string(), +/// top_level_domain: "org".to_string(), +/// author: "Acme Corp".to_string(), +/// app_name: "Frobnicator Plus".to_string(), /// }).unwrap(); /// /// let home_dir = etcetera::home_dir().unwrap(); /// /// assert_eq!( /// app_strategy.config_dir().strip_prefix(&home_dir), -/// Ok(Path::new("AppData/Roaming/Microsoft/File Explorer/config")) +/// Ok(Path::new("AppData/Roaming/Acme Corp/Frobnicator Plus/config")) /// ); /// assert_eq!( /// app_strategy.data_dir().strip_prefix(&home_dir), -/// Ok(Path::new("AppData/Roaming/Microsoft/File Explorer/data")) +/// Ok(Path::new("AppData/Roaming/Acme Corp/Frobnicator Plus/data")) /// ); /// assert_eq!( /// app_strategy.cache_dir().strip_prefix(&home_dir), -/// Ok(Path::new("AppData/Local/Microsoft/File Explorer/cache")) +/// Ok(Path::new("AppData/Local/Acme Corp/Frobnicator Plus/cache")) /// ); /// assert_eq!( /// app_strategy.state_dir(), diff --git a/src/app_strategy/xdg.rs b/src/app_strategy/xdg.rs index efaf012..a6264fc 100644 --- a/src/app_strategy/xdg.rs +++ b/src/app_strategy/xdg.rs @@ -20,29 +20,29 @@ use std::path::PathBuf; /// std::env::remove_var("XDG_RUNTIME_DIR"); /// /// let app_strategy = Xdg::new(AppStrategyArgs { -/// top_level_domain: "hm".to_string(), -/// author: "hisham".to_string(), -/// app_name: "htop".to_string(), +/// top_level_domain: "org".to_string(), +/// author: "Acme Corp".to_string(), +/// app_name: "Frobnicator Plus".to_string(), /// }).unwrap(); /// /// let home_dir = etcetera::home_dir().unwrap(); /// /// assert_eq!( /// app_strategy.config_dir().strip_prefix(&home_dir), -/// Ok(Path::new(".config/htop/") -/// )); +/// Ok(Path::new(".config/frobnicator-plus/")) +/// ); /// assert_eq!( /// app_strategy.data_dir().strip_prefix(&home_dir), -/// Ok(Path::new(".local/share/htop/") -/// )); +/// Ok(Path::new(".local/share/frobnicator-plus/")) +/// ); /// assert_eq!( /// app_strategy.cache_dir().strip_prefix(&home_dir), -/// Ok(Path::new(".cache/htop/") -/// )); +/// Ok(Path::new(".cache/frobnicator-plus/")) +/// ); /// assert_eq!( /// app_strategy.state_dir().unwrap().strip_prefix(&home_dir), -/// Ok(Path::new(".local/state/htop/") -/// )); +/// Ok(Path::new(".local/state/frobnicator-plus/")) +/// ); /// assert_eq!( /// app_strategy.runtime_dir(), /// None @@ -91,16 +91,31 @@ use std::path::PathBuf; /// std::env::set_var("XDG_RUNTIME_DIR", runtime_path); /// /// let app_strategy = Xdg::new(AppStrategyArgs { -/// top_level_domain: "hm".to_string(), -/// author: "hisham".to_string(), -/// app_name: "htop".to_string(), +/// top_level_domain: "org".to_string(), +/// author: "Acme Corp".to_string(), +/// app_name: "Frobnicator Plus".to_string(), /// }).unwrap(); /// -/// assert_eq!(app_strategy.config_dir(), Path::new(&format!("{}/htop/", config_path))); -/// assert_eq!(app_strategy.data_dir(), Path::new(&format!("{}/htop/", data_path))); -/// assert_eq!(app_strategy.cache_dir(), Path::new(&format!("{}/htop/", cache_path))); -/// assert_eq!(app_strategy.state_dir().unwrap(), Path::new(&format!("{}/htop/", state_path))); -/// assert_eq!(app_strategy.runtime_dir().unwrap(), Path::new(&format!("{}/htop/", runtime_path))); +/// assert_eq!( +/// app_strategy.config_dir(), +/// Path::new(&format!("{}/frobnicator-plus/", config_path)) +/// ); +/// assert_eq!( +/// app_strategy.data_dir(), +/// Path::new(&format!("{}/frobnicator-plus/", data_path)) +/// ); +/// assert_eq!( +/// app_strategy.cache_dir(), +/// Path::new(&format!("{}/frobnicator-plus/", cache_path)) +/// ); +/// assert_eq!( +/// app_strategy.state_dir().unwrap(), +/// Path::new(&format!("{}/frobnicator-plus/", state_path)) +/// ); +/// assert_eq!( +/// app_strategy.runtime_dir().unwrap(), +/// Path::new(&format!("{}/frobnicator-plus/", runtime_path)) +/// ); /// ``` /// /// The XDG spec requires that when the environment variables’ values are not absolute paths, their values should be ignored. This example exemplifies this behaviour: @@ -119,9 +134,9 @@ use std::path::PathBuf; /// std::env::set_var("XDG_RUNTIME_DIR", "relative_path/"); /// /// let app_strategy = Xdg::new(AppStrategyArgs { -/// top_level_domain: "hm".to_string(), -/// author: "hisham".to_string(), -/// app_name: "htop".to_string(), +/// top_level_domain: "org".to_string(), +/// author: "Acme Corp".to_string(), +/// app_name: "Frobnicator Plus".to_string(), /// }).unwrap(); /// /// let home_dir = etcetera::home_dir().unwrap(); @@ -129,20 +144,20 @@ use std::path::PathBuf; /// // We still get the default values. /// assert_eq!( /// app_strategy.config_dir().strip_prefix(&home_dir), -/// Ok(Path::new(".config/htop/") -/// )); +/// Ok(Path::new(".config/frobnicator-plus/")) +/// ); /// assert_eq!( /// app_strategy.data_dir().strip_prefix(&home_dir), -/// Ok(Path::new(".local/share/htop/") -/// )); +/// Ok(Path::new(".local/share/frobnicator-plus/")) +/// ); /// assert_eq!( /// app_strategy.cache_dir().strip_prefix(&home_dir), -/// Ok(Path::new(".cache/htop/") -/// )); +/// Ok(Path::new(".cache/frobnicator-plus/")) +/// ); /// assert_eq!( /// app_strategy.state_dir().unwrap().strip_prefix(&home_dir), -/// Ok(Path::new(".local/state/htop/") -/// )); +/// Ok(Path::new(".local/state/frobnicator-plus/")) +/// ); /// assert_eq!( /// app_strategy.runtime_dir(), /// None diff --git a/src/base_strategy/apple.rs b/src/base_strategy/apple.rs index 44325d1..79dd8b2 100644 --- a/src/base_strategy/apple.rs +++ b/src/base_strategy/apple.rs @@ -13,16 +13,16 @@ use std::path::PathBuf; /// /// assert_eq!( /// base_strategy.config_dir().strip_prefix(&home_dir), -/// Ok(Path::new("Library/Preferences/") -/// )); +/// Ok(Path::new("Library/Preferences/")) +/// ); /// assert_eq!( /// base_strategy.data_dir().strip_prefix(&home_dir), -/// Ok(Path::new("Library/Application Support/") -/// )); +/// Ok(Path::new("Library/Application Support/")) +/// ); /// assert_eq!( /// base_strategy.cache_dir().strip_prefix(&home_dir), -/// Ok(Path::new("Library/Caches/") -/// )); +/// Ok(Path::new("Library/Caches/")) +/// ); /// assert_eq!( /// base_strategy.state_dir(), /// None diff --git a/src/base_strategy/xdg.rs b/src/base_strategy/xdg.rs index 925e235..8900d44 100644 --- a/src/base_strategy/xdg.rs +++ b/src/base_strategy/xdg.rs @@ -22,20 +22,20 @@ use std::path::PathBuf; /// /// assert_eq!( /// base_strategy.config_dir().strip_prefix(&home_dir), -/// Ok(Path::new(".config/") -/// )); +/// Ok(Path::new(".config/")) +/// ); /// assert_eq!( /// base_strategy.data_dir().strip_prefix(&home_dir), -/// Ok(Path::new(".local/share/") -/// )); +/// Ok(Path::new(".local/share/")) +/// ); /// assert_eq!( /// base_strategy.cache_dir().strip_prefix(&home_dir), -/// Ok(Path::new(".cache/") -/// )); +/// Ok(Path::new(".cache/")) +/// ); /// assert_eq!( /// base_strategy.state_dir().unwrap().strip_prefix(&home_dir), -/// Ok(Path::new(".local/state") -/// )); +/// Ok(Path::new(".local/state")) +/// ); /// assert_eq!( /// base_strategy.runtime_dir(), /// None @@ -84,11 +84,26 @@ use std::path::PathBuf; /// /// let base_strategy = Xdg::new().unwrap(); /// -/// assert_eq!(base_strategy.config_dir(), Path::new(config_path)); -/// assert_eq!(base_strategy.data_dir(), Path::new(data_path)); -/// assert_eq!(base_strategy.cache_dir(), Path::new(cache_path)); -/// assert_eq!(base_strategy.state_dir().unwrap(), Path::new(state_path)); -/// assert_eq!(base_strategy.runtime_dir().unwrap(), Path::new(runtime_path)); +/// assert_eq!( +/// base_strategy.config_dir(), +/// Path::new(config_path) +/// ); +/// assert_eq!( +/// base_strategy.data_dir(), +/// Path::new(data_path) +/// ); +/// assert_eq!( +/// base_strategy.cache_dir(), +/// Path::new(cache_path) +/// ); +/// assert_eq!( +/// base_strategy.state_dir().unwrap(), +/// Path::new(state_path) +/// ); +/// assert_eq!( +/// base_strategy.runtime_dir().unwrap(), +/// Path::new(runtime_path) +/// ); /// ``` /// /// The specification states that: @@ -114,20 +129,20 @@ use std::path::PathBuf; /// /// assert_eq!( /// base_strategy.config_dir().strip_prefix(&home_dir), -/// Ok(Path::new(".config/") -/// )); +/// Ok(Path::new(".config/")) +/// ); /// assert_eq!( /// base_strategy.data_dir().strip_prefix(&home_dir), -/// Ok(Path::new(".local/share/") -/// )); +/// Ok(Path::new(".local/share/")) +/// ); /// assert_eq!( /// base_strategy.cache_dir().strip_prefix(&home_dir), -/// Ok(Path::new(".cache/") -/// )); +/// Ok(Path::new(".cache/")) +/// ); /// assert_eq!( /// base_strategy.state_dir().unwrap().strip_prefix(&home_dir), -/// Ok(Path::new(".local/state/") -/// )); +/// Ok(Path::new(".local/state/")) +/// ); /// assert_eq!( /// base_strategy.runtime_dir(), /// None diff --git a/src/lib.rs b/src/lib.rs index a1ab119..36edc08 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,9 @@ -//! This is a Rust library that aims to allow you to determine the locations of configuration, cache and data files for your application. Existing Rust libraries generally do not give you a choice in terms of which standards/conventions (Etcetera calls these ‘strategies’) they follow. Etcetera, on the other hand, gives you the choice. +//! This is a Rust library that allows you to determine the locations of configuration, data, cache & other files for your application. Existing Rust libraries generally do not give you a choice in terms of which standards/conventions (Etcetera calls these ‘strategies’) they follow. Etcetera, on the other hand, gives you the choice. //! -//! If you want to get started quickly, you can use the provided convenience functions that use the default strategies (as determined arbitrarily by yours truly) for each OS. +//! # Strategies +//! If you want to get started quickly, you can use the provided convenience functions that use the default strategies (as determined arbitrarily by yours truly) or the native strategies for each OS. //! +//! ## BaseStrategy //! This first example is for when you just want the path to a configuration, data or cache directory (this is called a ‘base strategy’): //! //! ``` @@ -14,11 +16,19 @@ //! let data_dir = strategy.data_dir(); //! let cache_dir = strategy.cache_dir(); //! let state_dir = strategy.state_dir(); +//! let runtime_dir = strategy.runtime_dir(); //! ``` //! +//! ## AppStrategy //! Here is an example where you provide Etcetera with some basic information about your application, and Etcetera will in turn give you a path that includes this information (this is called an ‘app strategy’). //! -//! Let’s take an application created by Acme Corp with the name Frobnicator and the top-level domain of `.org` as an example. With Apple’s strategy, configuration files would be located in `~/Library/Preferences/org.acmecorp.Frobnicator/`. XDG would place these in `~/.config/frobnicator/`, on the other hand. Etcetera takes care of the distinctions. +//! Let’s take an application created by Acme Corp with the name Frobnicator Plus and the top-level domain of `.org` as an example. +//! - XDG strategy would place these in `~/.config/frobnicator-plus`. +//! - Unix strategy would place these in `~/.frobnicator-plus`. +//! - Apple strategy would place these in `~/Library/Preferences/org.acmecorp.FrobnicatorPlus`. +//! - Windows strategy would place these in `~\AppData\Roaming\Acme Corp\Frobnicator Plus`. +//! +//! Etcetera takes care of the distinctions. //! //! ``` //! use etcetera::app_strategy; @@ -28,15 +38,29 @@ //! let strategy = app_strategy::choose_app_strategy(AppStrategyArgs { //! top_level_domain: "org".to_string(), //! author: "Acme Corp".to_string(), -//! app_name: "Frobnicator".to_string(), +//! app_name: "Frobnicator Plus".to_string(), //! }).unwrap(); //! //! let config_dir = strategy.config_dir(); //! let data_dir = strategy.data_dir(); //! let cache_dir = strategy.cache_dir(); //! let state_dir = strategy.state_dir(); +//! let runtime_dir = strategy.runtime_dir(); //! ``` //! +//! ## Native Strategy +//! +//! `choose_base_strategy()` and `choose_app_strategy()` will use the `XDG` strategy on Linux & macOS, and the `Windows` strategy on Windows. +//! This is used by most CLI tools & some GUI tools on each platform. +//! +//! If you're developing a GUI application, you might want to use the "Standard directories" on macOS by using `choose_native_strategy()` instead. +//! Note that if your application expects the user to modify the configuration files, you still should prefer the `XDG` strategy on macOS. +//! +//! ## Custom Strategies +//! +//! You aren’t limited to the built-in strategies, however – you can implement the relevant traits yourself. Please consider contributing these back, as the more preset strategies there are, the better. +//! +//! # More Examples //! Say you were a hardened Unix veteran, and didn’t want to have any of this XDG nonsense, clutter in the home directory be damned! Instead of using `choose_app_strategy` or `choose_base_strategy`, you can pick a strategy yourself. Here’s an example using the [`Unix`](app_strategy/struct.Unix.html) strategy – see its documentation to see what kind of folder structures it produces: //! //! ``` @@ -75,8 +99,6 @@ //! //! assert_eq!(config_dir.join("config.toml"), config_file); //! ``` -//! -//! You aren’t limited to the built-in strategies, however – you can implement the relevant traits yourself. Please consider contributing these back, as the more preset strategies there are, the better. #![warn(missing_docs, rust_2018_idioms, missing_debug_implementations)] @@ -89,6 +111,13 @@ pub fn home_dir() -> Result { } /// This error occurs when the home directory cannot be located. -#[derive(Debug, thiserror::Error)] -#[error("could not locate home directory")] +#[derive(Debug)] pub struct HomeDirError; + +impl std::fmt::Display for HomeDirError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "could not locate home directory") + } +} + +impl std::error::Error for HomeDirError {}