mirror of
https://github.com/dtolnay/cargo-expand.git
synced 2025-04-02 20:37:37 +03:00
Strip unused code from etcetera
This commit is contained in:
parent
6a2b06f3be
commit
d876f6e238
10 changed files with 6 additions and 1257 deletions
|
@ -1,175 +0,0 @@
|
|||
//! These strategies require you to provide some information on your application, and they will in turn locate the configuration/data/cache directory specifically for your application.
|
||||
|
||||
use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::etcetera::HomeDirError;
|
||||
|
||||
/// The arguments to the creator method of an [`AppStrategy`](trait.AppStrategy.html).
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct AppStrategyArgs {
|
||||
/// The top level domain of the application, e.g. `com`, `org`, or `io.github`.
|
||||
pub top_level_domain: String,
|
||||
/// The name of the author of the application.
|
||||
pub author: String,
|
||||
/// The application’s name. This should be capitalised if appropriate.
|
||||
pub app_name: String,
|
||||
}
|
||||
|
||||
impl AppStrategyArgs {
|
||||
/// Constructs a bunde identifier from an `AppStrategyArgs`.
|
||||
///
|
||||
/// ```
|
||||
/// use etcetera::app_strategy::AppStrategyArgs;
|
||||
///
|
||||
/// let strategy_args = AppStrategyArgs {
|
||||
/// top_level_domain: "org".to_string(),
|
||||
/// author: "Acme Corp".to_string(),
|
||||
/// app_name: "Frobnicator Plus".to_string(),
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(strategy_args.bundle_id(), "org.acme-corp.Frobnicator-Plus".to_string());
|
||||
/// ```
|
||||
pub fn bundle_id(&self) -> String {
|
||||
let author = self.author.to_lowercase().replace(' ', "-");
|
||||
let app_name = self.app_name.replace(' ', "-");
|
||||
let mut parts = vec![
|
||||
self.top_level_domain.as_str(),
|
||||
author.as_str(),
|
||||
app_name.as_str(),
|
||||
];
|
||||
parts.retain(|part| !part.is_empty());
|
||||
parts.join(".")
|
||||
}
|
||||
|
||||
/// Returns a ‘unixy’ version of the application’s name, akin to what would usually be used as a binary name.
|
||||
///
|
||||
/// ```
|
||||
/// use etcetera::app_strategy::AppStrategyArgs;
|
||||
///
|
||||
/// let strategy_args = AppStrategyArgs {
|
||||
/// top_level_domain: "org".to_string(),
|
||||
/// author: "Acme Corp".to_string(),
|
||||
/// app_name: "Frobnicator Plus".to_string(),
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(strategy_args.unixy_name(), "frobnicator-plus".to_string());
|
||||
/// ```
|
||||
pub fn unixy_name(&self) -> String {
|
||||
self.app_name.to_lowercase().replace(' ', "-")
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
pub trait AppStrategy {
|
||||
/// Gets the home directory of the current user.
|
||||
fn home_dir(&self) -> &Path;
|
||||
|
||||
/// Gets the configuration directory for your application.
|
||||
fn config_dir(&self) -> PathBuf;
|
||||
|
||||
/// Gets the data directory for your application.
|
||||
fn data_dir(&self) -> PathBuf;
|
||||
|
||||
/// Gets the cache directory for your application.
|
||||
fn cache_dir(&self) -> PathBuf;
|
||||
|
||||
/// Gets the state directory for your application.
|
||||
/// Currently, only the [`Xdg`](struct.Xdg.html) & [`Unix`](struct.Unix.html) strategies support
|
||||
/// this.
|
||||
fn state_dir(&self) -> Option<PathBuf>;
|
||||
|
||||
/// Gets the runtime directory for your application.
|
||||
/// Currently, only the [`Xdg`](struct.Xdg.html) & [`Unix`](struct.Unix.html) strategies support
|
||||
/// this.
|
||||
///
|
||||
/// Note: The [XDG Base Directory Specification](spec) places additional requirements on this
|
||||
/// directory related to ownership, permissions, and persistence. This library does not check
|
||||
/// these requirements.
|
||||
///
|
||||
/// [spec]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
fn runtime_dir(&self) -> Option<PathBuf>;
|
||||
|
||||
/// Constructs a path inside your application’s configuration directory to which a path of your choice has been appended.
|
||||
fn in_config_dir<P: AsRef<OsStr>>(&self, path: P) -> PathBuf {
|
||||
in_dir_method!(self, path, config_dir)
|
||||
}
|
||||
|
||||
/// Constructs a path inside your application’s data directory to which a path of your choice has been appended.
|
||||
fn in_data_dir<P: AsRef<OsStr>>(&self, path: P) -> PathBuf {
|
||||
in_dir_method!(self, path, data_dir)
|
||||
}
|
||||
|
||||
/// Constructs a path inside your application’s cache directory to which a path of your choice has been appended.
|
||||
fn in_cache_dir<P: AsRef<OsStr>>(&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.
|
||||
///
|
||||
/// Currently, this is only implemented for the [`Xdg`](struct.Xdg.html) strategy.
|
||||
fn in_state_dir<P: AsRef<OsStr>>(&self, path: P) -> Option<PathBuf> {
|
||||
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.
|
||||
/// Currently, only the [`Xdg`](struct.Xdg.html) & [`Unix`](struct.Unix.html) strategies support
|
||||
/// this.
|
||||
///
|
||||
/// See the note in [`runtime_dir`](#method.runtime_dir) for more information.
|
||||
fn in_runtime_dir<P: AsRef<OsStr>>(&self, path: P) -> Option<PathBuf> {
|
||||
in_dir_method!(opt: self, path, runtime_dir)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! create_strategies {
|
||||
($native: ty, $app: ty) => {
|
||||
/// Returns the current OS’s native [`AppStrategy`](trait.AppStrategy.html).
|
||||
/// This uses the [`Windows`](struct.Windows.html) strategy on Windows, [`Apple`](struct.Apple.html) on macOS & iOS, and [`Xdg`](struct.Xdg.html) everywhere else.
|
||||
/// This is the convention used by most GUI applications.
|
||||
pub fn choose_native_strategy(args: AppStrategyArgs) -> Result<$native, HomeDirError> {
|
||||
<$native>::new(args)
|
||||
}
|
||||
|
||||
/// Returns the current OS’s default [`AppStrategy`](trait.AppStrategy.html).
|
||||
/// This uses the [`Windows`](struct.Windows.html) strategy on Windows, and [`Xdg`](struct.Xdg.html) everywhere else.
|
||||
/// This is the convention used by most CLI applications.
|
||||
pub fn choose_app_strategy(args: AppStrategyArgs) -> Result<$app, HomeDirError> {
|
||||
<$app>::new(args)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_os = "windows")] {
|
||||
create_strategies!(Windows, Windows);
|
||||
} else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
|
||||
create_strategies!(Apple, Xdg);
|
||||
} else {
|
||||
create_strategies!(Xdg, Xdg);
|
||||
}
|
||||
}
|
||||
|
||||
mod apple;
|
||||
mod unix;
|
||||
mod windows;
|
||||
mod xdg;
|
||||
|
||||
pub use apple::Apple;
|
||||
pub use unix::Unix;
|
||||
pub use windows::Windows;
|
||||
pub use xdg::Xdg;
|
|
@ -1,86 +0,0 @@
|
|||
use crate::etcetera::base_strategy::BaseStrategy;
|
||||
use crate::etcetera::{base_strategy, HomeDirError};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// This is the strategy created by Apple for use on macOS and iOS devices. It is always used by GUI apps on macOS, and is sometimes used by command-line applications there too. iOS only has GUIs, so all iOS applications follow this strategy. The specification is available [here](https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html#//apple_ref/doc/uid/TP40010672-CH2-SW1).
|
||||
///
|
||||
/// ```
|
||||
/// use etcetera::app_strategy::AppStrategy;
|
||||
/// use etcetera::app_strategy::AppStrategyArgs;
|
||||
/// use etcetera::app_strategy::Apple;
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// let app_strategy = Apple::new(AppStrategyArgs {
|
||||
/// 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.home_dir(),
|
||||
/// &home_dir
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.config_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new("Library/Preferences/org.acme-corp.Frobnicator-Plus/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.data_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new("Library/Application Support/org.acme-corp.Frobnicator-Plus/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.cache_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new("Library/Caches/org.acme-corp.Frobnicator-Plus/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.state_dir(),
|
||||
/// None
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.runtime_dir(),
|
||||
/// None
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Apple {
|
||||
base_strategy: base_strategy::Apple,
|
||||
bundle_id: String,
|
||||
}
|
||||
|
||||
impl Apple {
|
||||
/// Create a new Apple AppStrategy
|
||||
pub fn new(args: super::AppStrategyArgs) -> Result<Self, HomeDirError> {
|
||||
Ok(Self {
|
||||
base_strategy: base_strategy::Apple::new()?,
|
||||
bundle_id: args.bundle_id(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl super::AppStrategy for Apple {
|
||||
fn home_dir(&self) -> &Path {
|
||||
self.base_strategy.home_dir()
|
||||
}
|
||||
|
||||
fn config_dir(&self) -> PathBuf {
|
||||
self.base_strategy.config_dir().join(&self.bundle_id)
|
||||
}
|
||||
|
||||
fn data_dir(&self) -> PathBuf {
|
||||
self.base_strategy.data_dir().join(&self.bundle_id)
|
||||
}
|
||||
|
||||
fn cache_dir(&self) -> PathBuf {
|
||||
self.base_strategy.cache_dir().join(&self.bundle_id)
|
||||
}
|
||||
|
||||
fn state_dir(&self) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
fn runtime_dir(&self) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::etcetera::HomeDirError;
|
||||
|
||||
/// This strategy has no standard or official specification. It has arisen over time through hundreds of Unixy tools. Vim and Cargo are notable examples whose configuration/data/cache directory layouts are similar to those created by this strategy.
|
||||
///
|
||||
/// ```
|
||||
/// use etcetera::app_strategy::AppStrategy;
|
||||
/// use etcetera::app_strategy::AppStrategyArgs;
|
||||
/// use etcetera::app_strategy::Unix;
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// let app_strategy = Unix::new(AppStrategyArgs {
|
||||
/// 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.home_dir(),
|
||||
/// &home_dir
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.config_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".frobnicator-plus/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.data_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".frobnicator-plus/data/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.cache_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".frobnicator-plus/cache/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.state_dir().unwrap().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".frobnicator-plus/state/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.runtime_dir().unwrap().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".frobnicator-plus/runtime/"))
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Unix {
|
||||
// This is `.frobnicator-plus` in the above example.
|
||||
home_dir: PathBuf,
|
||||
unixy_name: String,
|
||||
}
|
||||
|
||||
impl Unix {
|
||||
/// Create a new Unix AppStrategy
|
||||
pub fn new(args: super::AppStrategyArgs) -> Result<Self, HomeDirError> {
|
||||
Ok(Self {
|
||||
home_dir: crate::etcetera::home_dir()?,
|
||||
unixy_name: format!(".{}", args.unixy_name()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl super::AppStrategy for Unix {
|
||||
fn home_dir(&self) -> &Path {
|
||||
&self.home_dir
|
||||
}
|
||||
|
||||
fn config_dir(&self) -> PathBuf {
|
||||
self.home_dir.join(&self.unixy_name)
|
||||
}
|
||||
|
||||
fn data_dir(&self) -> PathBuf {
|
||||
self.home_dir.join(&self.unixy_name).join("data/")
|
||||
}
|
||||
|
||||
fn cache_dir(&self) -> PathBuf {
|
||||
self.home_dir.join(&self.unixy_name).join("cache/")
|
||||
}
|
||||
|
||||
fn state_dir(&self) -> Option<PathBuf> {
|
||||
Some(self.home_dir.join(&self.unixy_name).join("state/"))
|
||||
}
|
||||
|
||||
fn runtime_dir(&self) -> Option<PathBuf> {
|
||||
Some(self.home_dir.join(&self.unixy_name).join("runtime/"))
|
||||
}
|
||||
}
|
|
@ -1,166 +0,0 @@
|
|||
use crate::etcetera::base_strategy::BaseStrategy;
|
||||
use crate::etcetera::{base_strategy, HomeDirError};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// This strategy follows Windows’ conventions. It seems that all Windows GUI apps, and some command-line ones follow this pattern. The specification is available [here](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid).
|
||||
///
|
||||
/// This initial example removes all the relevant environment variables to show the strategy’s use of the:
|
||||
/// - (on Windows) SHGetKnownFolderPath API.
|
||||
/// - (on non-Windows) Windows default directories.
|
||||
///
|
||||
/// ```
|
||||
/// use etcetera::app_strategy::AppStrategy;
|
||||
/// use etcetera::app_strategy::AppStrategyArgs;
|
||||
/// use etcetera::app_strategy::Windows;
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// // Remove the environment variables that the strategy reads from.
|
||||
/// std::env::remove_var("USERPROFILE");
|
||||
/// std::env::remove_var("APPDATA");
|
||||
/// std::env::remove_var("LOCALAPPDATA");
|
||||
///
|
||||
/// let app_strategy = Windows::new(AppStrategyArgs {
|
||||
/// 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.home_dir(),
|
||||
/// &home_dir
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.config_dir().strip_prefix(&home_dir),
|
||||
/// 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/Acme Corp/Frobnicator Plus/data"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.cache_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new("AppData/Local/Acme Corp/Frobnicator Plus/cache"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.state_dir(),
|
||||
/// None
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.runtime_dir(),
|
||||
/// None
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// This next example gives the environment variables values:
|
||||
///
|
||||
/// ```
|
||||
/// use etcetera::app_strategy::AppStrategy;
|
||||
/// use etcetera::app_strategy::AppStrategyArgs;
|
||||
/// use etcetera::app_strategy::Windows;
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// let home_path = if cfg!(windows) {
|
||||
/// "C:\\my_home_location\\".to_string()
|
||||
/// } else {
|
||||
/// etcetera::home_dir().unwrap().to_string_lossy().to_string()
|
||||
/// };
|
||||
/// let data_path = if cfg!(windows) {
|
||||
/// "C:\\my_data_location\\"
|
||||
/// } else {
|
||||
/// "/my_data_location/"
|
||||
/// };
|
||||
/// let cache_path = if cfg!(windows) {
|
||||
/// "C:\\my_cache_location\\"
|
||||
/// } else {
|
||||
/// "/my_cache_location/"
|
||||
/// };
|
||||
///
|
||||
/// std::env::set_var("USERPROFILE", &home_path);
|
||||
/// std::env::set_var("APPDATA", data_path);
|
||||
/// std::env::set_var("LOCALAPPDATA", cache_path);
|
||||
///
|
||||
/// let app_strategy = Windows::new(AppStrategyArgs {
|
||||
/// top_level_domain: "org".to_string(),
|
||||
/// author: "Acme Corp".to_string(),
|
||||
/// app_name: "Frobnicator Plus".to_string(),
|
||||
/// }).unwrap();
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// app_strategy.home_dir(),
|
||||
/// Path::new(&home_path)
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.config_dir(),
|
||||
/// Path::new(&format!("{}/Acme Corp/Frobnicator Plus/config", data_path))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.data_dir(),
|
||||
/// Path::new(&format!("{}/Acme Corp/Frobnicator Plus/data", data_path))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.cache_dir(),
|
||||
/// Path::new(&format!("{}/Acme Corp/Frobnicator Plus/cache", cache_path))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.state_dir(),
|
||||
/// None
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.runtime_dir(),
|
||||
/// None
|
||||
/// );
|
||||
/// ```
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Windows {
|
||||
base_strategy: base_strategy::Windows,
|
||||
author_app_name_path: PathBuf,
|
||||
}
|
||||
|
||||
macro_rules! dir_method {
|
||||
($self: ident, $base_strategy_method: ident, $subfolder_name: expr) => {{
|
||||
let mut path = $self.base_strategy.$base_strategy_method();
|
||||
path.push(&$self.author_app_name_path);
|
||||
path.push($subfolder_name);
|
||||
|
||||
path
|
||||
}};
|
||||
}
|
||||
|
||||
impl Windows {
|
||||
/// Create a new Windows AppStrategy
|
||||
pub fn new(args: super::AppStrategyArgs) -> Result<Self, HomeDirError> {
|
||||
Ok(Self {
|
||||
base_strategy: base_strategy::Windows::new()?,
|
||||
author_app_name_path: PathBuf::from(args.author).join(args.app_name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl super::AppStrategy for Windows {
|
||||
fn home_dir(&self) -> &Path {
|
||||
self.base_strategy.home_dir()
|
||||
}
|
||||
|
||||
fn config_dir(&self) -> PathBuf {
|
||||
dir_method!(self, config_dir, "config")
|
||||
}
|
||||
|
||||
fn data_dir(&self) -> PathBuf {
|
||||
dir_method!(self, data_dir, "data")
|
||||
}
|
||||
|
||||
fn cache_dir(&self) -> PathBuf {
|
||||
dir_method!(self, cache_dir, "cache")
|
||||
}
|
||||
|
||||
fn state_dir(&self) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
fn runtime_dir(&self) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
}
|
|
@ -1,217 +0,0 @@
|
|||
use crate::etcetera::base_strategy::BaseStrategy;
|
||||
use crate::etcetera::{base_strategy, HomeDirError};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// This strategy implements the [XDG Base Directories Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html). It is the most common on Linux, but is increasingly being adopted elsewhere.
|
||||
///
|
||||
/// This initial example removes all the XDG environment variables to show the strategy’s use of the XDG default directories.
|
||||
///
|
||||
/// ```
|
||||
/// use etcetera::app_strategy::AppStrategy;
|
||||
/// use etcetera::app_strategy::AppStrategyArgs;
|
||||
/// use etcetera::app_strategy::Xdg;
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// // Remove the environment variables that the strategy reads from.
|
||||
/// std::env::remove_var("XDG_CONFIG_HOME");
|
||||
/// std::env::remove_var("XDG_DATA_HOME");
|
||||
/// std::env::remove_var("XDG_CACHE_HOME");
|
||||
/// std::env::remove_var("XDG_STATE_HOME");
|
||||
/// std::env::remove_var("XDG_RUNTIME_DIR");
|
||||
///
|
||||
/// let app_strategy = Xdg::new(AppStrategyArgs {
|
||||
/// 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.home_dir(),
|
||||
/// &home_dir
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.config_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".config/frobnicator-plus/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.data_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".local/share/frobnicator-plus/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.cache_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".cache/frobnicator-plus/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.state_dir().unwrap().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".local/state/frobnicator-plus/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.runtime_dir(),
|
||||
/// None
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// This next example gives the environment variables values:
|
||||
///
|
||||
/// ```
|
||||
/// use etcetera::app_strategy::AppStrategy;
|
||||
/// use etcetera::app_strategy::AppStrategyArgs;
|
||||
/// use etcetera::app_strategy::Xdg;
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// // We need to conditionally set these to ensure that they are absolute paths both on Windows and other systems.
|
||||
/// let config_path = if cfg!(windows) {
|
||||
/// "C:\\my_config_location\\"
|
||||
/// } else {
|
||||
/// "/my_config_location/"
|
||||
/// };
|
||||
/// let data_path = if cfg!(windows) {
|
||||
/// "C:\\my_data_location\\"
|
||||
/// } else {
|
||||
/// "/my_data_location/"
|
||||
/// };
|
||||
/// let cache_path = if cfg!(windows) {
|
||||
/// "C:\\my_cache_location\\"
|
||||
/// } else {
|
||||
/// "/my_cache_location/"
|
||||
/// };
|
||||
/// let state_path = if cfg!(windows) {
|
||||
/// "C:\\my_state_location\\"
|
||||
/// } else {
|
||||
/// "/my_state_location/"
|
||||
/// };
|
||||
/// let runtime_path = if cfg!(windows) {
|
||||
/// "C:\\my_runtime_location\\"
|
||||
/// } else {
|
||||
/// "/my_runtime_location/"
|
||||
/// };
|
||||
///
|
||||
/// std::env::set_var("XDG_CONFIG_HOME", config_path);
|
||||
/// std::env::set_var("XDG_DATA_HOME", data_path);
|
||||
/// std::env::set_var("XDG_CACHE_HOME", cache_path);
|
||||
/// std::env::set_var("XDG_STATE_HOME", state_path);
|
||||
/// std::env::set_var("XDG_RUNTIME_DIR", runtime_path);
|
||||
///
|
||||
/// let app_strategy = Xdg::new(AppStrategyArgs {
|
||||
/// 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!("{}/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:
|
||||
///
|
||||
/// ```
|
||||
/// use etcetera::app_strategy::AppStrategy;
|
||||
/// use etcetera::app_strategy::AppStrategyArgs;
|
||||
/// use etcetera::app_strategy::Xdg;
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// // Remove the environment variables that the strategy reads from.
|
||||
/// std::env::set_var("XDG_CONFIG_HOME", "relative_path/");
|
||||
/// std::env::set_var("XDG_DATA_HOME", "./another_one/");
|
||||
/// std::env::set_var("XDG_CACHE_HOME", "yet_another/");
|
||||
/// std::env::set_var("XDG_STATE_HOME", "./and_another");
|
||||
/// std::env::set_var("XDG_RUNTIME_DIR", "relative_path/");
|
||||
///
|
||||
/// let app_strategy = Xdg::new(AppStrategyArgs {
|
||||
/// 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();
|
||||
///
|
||||
/// // We still get the default values.
|
||||
/// assert_eq!(
|
||||
/// app_strategy.config_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".config/frobnicator-plus/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.data_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".local/share/frobnicator-plus/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.cache_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".cache/frobnicator-plus/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.state_dir().unwrap().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".local/state/frobnicator-plus/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// app_strategy.runtime_dir(),
|
||||
/// None
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Xdg {
|
||||
base_strategy: base_strategy::Xdg,
|
||||
unixy_name: String,
|
||||
}
|
||||
|
||||
impl Xdg {
|
||||
/// Create a new Xdg AppStrategy
|
||||
pub fn new(args: super::AppStrategyArgs) -> Result<Self, HomeDirError> {
|
||||
Ok(Self {
|
||||
base_strategy: base_strategy::Xdg::new()?,
|
||||
unixy_name: args.unixy_name(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl super::AppStrategy for Xdg {
|
||||
fn home_dir(&self) -> &Path {
|
||||
self.base_strategy.home_dir()
|
||||
}
|
||||
|
||||
fn config_dir(&self) -> PathBuf {
|
||||
self.base_strategy.config_dir().join(&self.unixy_name)
|
||||
}
|
||||
|
||||
fn data_dir(&self) -> PathBuf {
|
||||
self.base_strategy.data_dir().join(&self.unixy_name)
|
||||
}
|
||||
|
||||
fn cache_dir(&self) -> PathBuf {
|
||||
self.base_strategy.cache_dir().join(&self.unixy_name)
|
||||
}
|
||||
|
||||
fn state_dir(&self) -> Option<PathBuf> {
|
||||
Some(
|
||||
self.base_strategy
|
||||
.state_dir()
|
||||
.unwrap()
|
||||
.join(&self.unixy_name),
|
||||
)
|
||||
}
|
||||
|
||||
fn runtime_dir(&self) -> Option<PathBuf> {
|
||||
self.base_strategy
|
||||
.runtime_dir()
|
||||
.map(|runtime_dir| runtime_dir.join(&self.unixy_name))
|
||||
}
|
||||
}
|
|
@ -1,49 +1,12 @@
|
|||
//! These strategies simply provide the user’s configuration, data, and cache directories, without knowing about the application specifically.
|
||||
|
||||
use crate::etcetera::HomeDirError;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Provides configuration, data, and cache directories of the current user.
|
||||
pub trait BaseStrategy {
|
||||
/// Gets the home directory of the current user.
|
||||
fn home_dir(&self) -> &Path;
|
||||
|
||||
/// Gets the user’s configuration directory.
|
||||
fn config_dir(&self) -> PathBuf;
|
||||
|
||||
/// Gets the user’s data directory.
|
||||
fn data_dir(&self) -> PathBuf;
|
||||
|
||||
/// Gets the user’s cache directory.
|
||||
fn cache_dir(&self) -> PathBuf;
|
||||
|
||||
/// Gets the user’s state directory.
|
||||
/// Currently, only the [`Xdg`](struct.Xdg.html) strategy supports this.
|
||||
fn state_dir(&self) -> Option<PathBuf>;
|
||||
|
||||
/// Gets the user’s runtime directory.
|
||||
/// Currently, only the [`Xdg`](struct.Xdg.html) strategy supports this.
|
||||
///
|
||||
/// Note: The [XDG Base Directory Specification](spec) places additional requirements on this
|
||||
/// directory related to ownership, permissions, and persistence. This library does not check
|
||||
/// these requirements.
|
||||
///
|
||||
/// [spec]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
fn runtime_dir(&self) -> Option<PathBuf>;
|
||||
}
|
||||
|
||||
macro_rules! create_strategies {
|
||||
($native: ty, $base: ty) => {
|
||||
/// Returns the current OS’s native [`BaseStrategy`](trait.BaseStrategy.html).
|
||||
/// This uses the [`Windows`](struct.Windows.html) strategy on Windows, [`Apple`](struct.Apple.html) on macOS & iOS, and [`Xdg`](struct.Xdg.html) everywhere else.
|
||||
/// This is the convention used by most GUI applications.
|
||||
pub fn choose_native_strategy() -> Result<$native, HomeDirError> {
|
||||
<$native>::new()
|
||||
}
|
||||
|
||||
/// Returns the current OS’s default [`BaseStrategy`](trait.BaseStrategy.html).
|
||||
/// This uses the [`Windows`](struct.Windows.html) strategy on Windows, and [`Xdg`](struct.Xdg.html) everywhere else.
|
||||
/// This is the convention used by most CLI applications.
|
||||
($base: ty) => {
|
||||
pub fn choose_base_strategy() -> Result<$base, HomeDirError> {
|
||||
<$base>::new()
|
||||
}
|
||||
|
@ -52,18 +15,16 @@ macro_rules! create_strategies {
|
|||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_os = "windows")] {
|
||||
create_strategies!(Windows, Windows);
|
||||
create_strategies!(Windows);
|
||||
} else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
|
||||
create_strategies!(Apple, Xdg);
|
||||
create_strategies!(Xdg);
|
||||
} else {
|
||||
create_strategies!(Xdg, Xdg);
|
||||
create_strategies!(Xdg);
|
||||
}
|
||||
}
|
||||
|
||||
mod apple;
|
||||
mod windows;
|
||||
mod xdg;
|
||||
|
||||
pub use apple::Apple;
|
||||
pub use windows::Windows;
|
||||
pub use xdg::Xdg;
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::etcetera::HomeDirError;
|
||||
|
||||
/// This is the strategy created by Apple for use on macOS and iOS devices. It is always used by GUI apps on macOS, and is sometimes used by command-line applications there too. iOS only has GUIs, so all iOS applications follow this strategy. The specification is available [here](https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html#//apple_ref/doc/uid/TP40010672-CH2-SW1).
|
||||
///
|
||||
/// ```
|
||||
/// use etcetera::base_strategy::Apple;
|
||||
/// use etcetera::base_strategy::BaseStrategy;
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// let base_strategy = Apple::new().unwrap();
|
||||
///
|
||||
/// let home_dir = etcetera::home_dir().unwrap();
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// base_strategy.home_dir(),
|
||||
/// &home_dir
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.config_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new("Library/Preferences/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.data_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new("Library/Application Support/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.cache_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new("Library/Caches/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.state_dir(),
|
||||
/// None
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.runtime_dir(),
|
||||
/// None
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Apple {
|
||||
home_dir: PathBuf,
|
||||
}
|
||||
impl Apple {
|
||||
/// Create a new Apple BaseStrategy
|
||||
pub fn new() -> Result<Self, HomeDirError> {
|
||||
Ok(Self {
|
||||
home_dir: crate::etcetera::home_dir()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl super::BaseStrategy for Apple {
|
||||
fn home_dir(&self) -> &Path {
|
||||
&self.home_dir
|
||||
}
|
||||
|
||||
fn config_dir(&self) -> PathBuf {
|
||||
self.home_dir.join("Library/Preferences/")
|
||||
}
|
||||
|
||||
fn data_dir(&self) -> PathBuf {
|
||||
self.home_dir.join("Library/Application Support/")
|
||||
}
|
||||
|
||||
fn cache_dir(&self) -> PathBuf {
|
||||
self.home_dir.join("Library/Caches/")
|
||||
}
|
||||
|
||||
fn state_dir(&self) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
fn runtime_dir(&self) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
}
|
|
@ -1,115 +1,12 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::etcetera::HomeDirError;
|
||||
|
||||
/// This strategy follows Windows’ conventions. It seems that all Windows GUI apps, and some command-line ones follow this pattern. The specification is available [here](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid).
|
||||
///
|
||||
/// This initial example removes all the relevant environment variables to show the strategy’s use of the:
|
||||
/// - (on Windows) SHGetKnownFolderPath API.
|
||||
/// - (on non-Windows) Windows default directories.
|
||||
///
|
||||
/// ```
|
||||
/// use etcetera::base_strategy::BaseStrategy;
|
||||
/// use etcetera::base_strategy::Windows;
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// // Remove the environment variables that the strategy reads from.
|
||||
/// std::env::remove_var("USERPROFILE");
|
||||
/// std::env::remove_var("APPDATA");
|
||||
/// std::env::remove_var("LOCALAPPDATA");
|
||||
///
|
||||
/// let base_strategy = Windows::new().unwrap();
|
||||
///
|
||||
/// let home_dir = etcetera::home_dir().unwrap();
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// base_strategy.home_dir(),
|
||||
/// &home_dir
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.config_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new("AppData/Roaming/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.data_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new("AppData/Roaming/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.cache_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new("AppData/Local/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.state_dir(),
|
||||
/// None
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.runtime_dir(),
|
||||
/// None
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// This next example gives the environment variables values:
|
||||
///
|
||||
/// ```
|
||||
/// use etcetera::base_strategy::BaseStrategy;
|
||||
/// use etcetera::base_strategy::Windows;
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// let home_path = if cfg!(windows) {
|
||||
/// "C:\\foo\\".to_string()
|
||||
/// } else {
|
||||
/// etcetera::home_dir().unwrap().to_string_lossy().to_string()
|
||||
/// };
|
||||
/// let data_path = if cfg!(windows) {
|
||||
/// "C:\\bar\\"
|
||||
/// } else {
|
||||
/// "/bar/"
|
||||
/// };
|
||||
/// let cache_path = if cfg!(windows) {
|
||||
/// "C:\\baz\\"
|
||||
/// } else {
|
||||
/// "/baz/"
|
||||
/// };
|
||||
///
|
||||
/// std::env::set_var("USERPROFILE", &home_path);
|
||||
/// std::env::set_var("APPDATA", data_path);
|
||||
/// std::env::set_var("LOCALAPPDATA", cache_path);
|
||||
///
|
||||
/// let base_strategy = Windows::new().unwrap();
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// base_strategy.home_dir(),
|
||||
/// Path::new(&home_path)
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.config_dir(),
|
||||
/// Path::new(data_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(),
|
||||
/// None
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.runtime_dir(),
|
||||
/// None
|
||||
/// );
|
||||
/// ```
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Windows {
|
||||
home_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl Windows {
|
||||
/// Create a new Windows BaseStrategy
|
||||
pub fn new() -> Result<Self, HomeDirError> {
|
||||
Ok(Self {
|
||||
home_dir: crate::etcetera::home_dir()?,
|
||||
|
@ -179,28 +76,8 @@ impl Windows {
|
|||
}
|
||||
|
||||
impl super::BaseStrategy for Windows {
|
||||
fn home_dir(&self) -> &Path {
|
||||
&self.home_dir
|
||||
}
|
||||
|
||||
fn config_dir(&self) -> PathBuf {
|
||||
self.data_dir()
|
||||
}
|
||||
|
||||
fn data_dir(&self) -> PathBuf {
|
||||
Self::dir_inner("APPDATA").unwrap_or_else(|| self.home_dir.join("AppData").join("Roaming"))
|
||||
}
|
||||
|
||||
fn cache_dir(&self) -> PathBuf {
|
||||
Self::dir_inner("LOCALAPPDATA")
|
||||
.unwrap_or_else(|| self.home_dir.join("AppData").join("Local"))
|
||||
}
|
||||
|
||||
fn state_dir(&self) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
fn runtime_dir(&self) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,163 +3,11 @@ use std::path::PathBuf;
|
|||
|
||||
use crate::etcetera::HomeDirError;
|
||||
|
||||
/// This strategy implements the [XDG Base Directories Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html). It is the most common on Linux, but is increasingly being adopted elsewhere.
|
||||
///
|
||||
/// This initial example removes all the XDG environment variables to show the strategy’s use of the XDG default directories.
|
||||
///
|
||||
/// ```
|
||||
/// use etcetera::base_strategy::BaseStrategy;
|
||||
/// use etcetera::base_strategy::Xdg;
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// // Remove the environment variables that the strategy reads from.
|
||||
/// std::env::remove_var("XDG_CONFIG_HOME");
|
||||
/// std::env::remove_var("XDG_DATA_HOME");
|
||||
/// std::env::remove_var("XDG_CACHE_HOME");
|
||||
/// std::env::remove_var("XDG_STATE_HOME");
|
||||
/// std::env::remove_var("XDG_RUNTIME_DIR");
|
||||
///
|
||||
/// let base_strategy = Xdg::new().unwrap();
|
||||
///
|
||||
/// let home_dir = etcetera::home_dir().unwrap();
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// base_strategy.home_dir(),
|
||||
/// &home_dir
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.config_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".config/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.data_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".local/share/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.cache_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".cache/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.state_dir().unwrap().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".local/state"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.runtime_dir(),
|
||||
/// None
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// This next example gives the environment variables values:
|
||||
///
|
||||
/// ```
|
||||
/// use etcetera::base_strategy::BaseStrategy;
|
||||
/// use etcetera::base_strategy::Xdg;
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// // We need to conditionally set these to ensure that they are absolute paths both on Windows and other systems.
|
||||
/// let config_path = if cfg!(windows) {
|
||||
/// "C:\\foo\\"
|
||||
/// } else {
|
||||
/// "/foo/"
|
||||
/// };
|
||||
/// let data_path = if cfg!(windows) {
|
||||
/// "C:\\bar\\"
|
||||
/// } else {
|
||||
/// "/bar/"
|
||||
/// };
|
||||
/// let cache_path = if cfg!(windows) {
|
||||
/// "C:\\baz\\"
|
||||
/// } else {
|
||||
/// "/baz/"
|
||||
/// };
|
||||
/// let state_path = if cfg!(windows) {
|
||||
/// "C:\\foobar\\"
|
||||
/// } else {
|
||||
/// "/foobar/"
|
||||
/// };
|
||||
/// let runtime_path = if cfg!(windows) {
|
||||
/// "C:\\qux\\"
|
||||
/// } else {
|
||||
/// "/qux/"
|
||||
/// };
|
||||
///
|
||||
/// std::env::set_var("XDG_CONFIG_HOME", config_path);
|
||||
/// std::env::set_var("XDG_DATA_HOME", data_path);
|
||||
/// std::env::set_var("XDG_CACHE_HOME", cache_path);
|
||||
/// std::env::set_var("XDG_STATE_HOME", state_path);
|
||||
/// std::env::set_var("XDG_RUNTIME_DIR", runtime_path);
|
||||
///
|
||||
/// 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)
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// The XDG spec requires that when the environment variables’ values are not absolute paths, their values should be ignored. This example exemplifies this behaviour:
|
||||
///
|
||||
/// ```
|
||||
/// use etcetera::base_strategy::BaseStrategy;
|
||||
/// use etcetera::base_strategy::Xdg;
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// // Remove the environment variables that the strategy reads from.
|
||||
/// std::env::set_var("XDG_CONFIG_HOME", "foo/");
|
||||
/// std::env::set_var("XDG_DATA_HOME", "bar/");
|
||||
/// std::env::set_var("XDG_CACHE_HOME", "baz/");
|
||||
/// std::env::set_var("XDG_STATE_HOME", "foobar/");
|
||||
/// std::env::set_var("XDG_RUNTIME_DIR", "qux/");
|
||||
///
|
||||
/// let base_strategy = Xdg::new().unwrap();
|
||||
///
|
||||
/// let home_dir = etcetera::home_dir().unwrap();
|
||||
///
|
||||
/// // We still get the default values.
|
||||
/// assert_eq!(
|
||||
/// base_strategy.config_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".config/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.data_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".local/share/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.cache_dir().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".cache/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.state_dir().unwrap().strip_prefix(&home_dir),
|
||||
/// Ok(Path::new(".local/state/"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// base_strategy.runtime_dir(),
|
||||
/// None
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Xdg {
|
||||
home_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl Xdg {
|
||||
/// Create a new Xdg BaseStrategy
|
||||
pub fn new() -> Result<Self, HomeDirError> {
|
||||
Ok(Self {
|
||||
home_dir: crate::etcetera::home_dir()?,
|
||||
|
@ -185,27 +33,7 @@ impl Xdg {
|
|||
}
|
||||
|
||||
impl super::BaseStrategy for Xdg {
|
||||
fn home_dir(&self) -> &Path {
|
||||
&self.home_dir
|
||||
}
|
||||
|
||||
fn config_dir(&self) -> PathBuf {
|
||||
self.env_var_or_default("XDG_CONFIG_HOME", ".config/")
|
||||
}
|
||||
|
||||
fn data_dir(&self) -> PathBuf {
|
||||
self.env_var_or_default("XDG_DATA_HOME", ".local/share/")
|
||||
}
|
||||
|
||||
fn cache_dir(&self) -> PathBuf {
|
||||
self.env_var_or_default("XDG_CACHE_HOME", ".cache/")
|
||||
}
|
||||
|
||||
fn state_dir(&self) -> Option<PathBuf> {
|
||||
Some(self.env_var_or_default("XDG_STATE_HOME", ".local/state/"))
|
||||
}
|
||||
|
||||
fn runtime_dir(&self) -> Option<PathBuf> {
|
||||
Self::env_var_or_none("XDG_RUNTIME_DIR")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,119 +1,11 @@
|
|||
//! 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 they follow.
|
||||
//! Etcetera, on the other hand, gives you the choice.
|
||||
//!
|
||||
//! # Conventions
|
||||
//! Etcetera supports the following conventions:
|
||||
//! - the [XDG base directory](https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html)
|
||||
//! - Apple's [Standard Directories](https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html)
|
||||
//! - Window's [Known Folder Locations](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid)
|
||||
//! - the "Unix Single-folder Convention" i.e. everything in `~/.myapp`
|
||||
//!
|
||||
//! # Strategies
|
||||
//! If you want to get started quickly, you can use the following convenience functions that use the default strategies (as determined arbitrarily by yours truly) or the native strategies for each OS.
|
||||
//!
|
||||
//! ## BaseStrategy
|
||||
//! If you just want to get the path to a configuration, data, cache or another directory, you can use the `choose_base_strategy` function.
|
||||
//!
|
||||
//! ```
|
||||
//! use etcetera::{choose_base_strategy, BaseStrategy};
|
||||
//!
|
||||
//! let strategy = choose_base_strategy().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();
|
||||
//! ```
|
||||
//!
|
||||
//! ## AppStrategy
|
||||
//! If you want to get the path to a configuration, data, cache or another directory, and you want to follow the naming conventions for your application, you can use the `choose_app_strategy` function.
|
||||
//!
|
||||
//! Let’s take an application created by `Acme Corp` with the name `Frobnicator Plus` and the top-level domain of `jrg` 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.acme-corp.Frobnicator-Plus`.
|
||||
//! - Windows strategy would place these in `~\AppData\Roaming\Acme Corp\Frobnicator Plus`.
|
||||
//!
|
||||
//! ```
|
||||
//! use etcetera::{choose_app_strategy, AppStrategy, AppStrategyArgs};
|
||||
//!
|
||||
//! let strategy = choose_app_strategy(AppStrategyArgs {
|
||||
//! top_level_domain: "org".to_string(),
|
||||
//! author: "Acme Corp".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 should still prefer the `XDG` strategy on macOS.
|
||||
//!
|
||||
//! ## Custom Conventions
|
||||
//!
|
||||
//! You aren’t limited to the built-in conventions – you can implement the relevant traits yourself. Please consider contributing these back, as the more preset conventions 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:
|
||||
//!
|
||||
//! ```
|
||||
//! use etcetera::{app_strategy, AppStrategy, AppStrategyArgs};
|
||||
//!
|
||||
//! let strategy = app_strategy::Unix::new(AppStrategyArgs {
|
||||
//! top_level_domain: "com".to_string(),
|
||||
//! author: "Hardened Unix Veteran Who Likes Short Command Names".to_string(),
|
||||
//! app_name: "wry".to_string(),
|
||||
//! }).unwrap();
|
||||
//!
|
||||
//! let config_dir = strategy.config_dir(); // produces ~/.wry/
|
||||
//! // et cetera.
|
||||
//! ```
|
||||
//!
|
||||
//! Oftentimes the location of a configuration, data or cache directory is needed solely to create a path that starts inside it. For this purpose, [`AppStrategy`](app_strategy/trait.AppStrategy.html) implements a couple of convenience methods for you:
|
||||
//!
|
||||
//! ```
|
||||
//! use etcetera::{choose_app_strategy, AppStrategy, AppStrategyArgs};
|
||||
//!
|
||||
//! let strategy = choose_app_strategy(AppStrategyArgs {
|
||||
//! top_level_domain: "org".to_string(),
|
||||
//! author: "Acme Corp".to_string(),
|
||||
//! app_name: "Frobnicator".to_string(),
|
||||
//! }).unwrap();
|
||||
//!
|
||||
//! // Path to configuration directory.
|
||||
//! let config_dir = strategy.config_dir();
|
||||
//!
|
||||
//! // Path to config.toml inside the configuration directory.
|
||||
//! let config_file = strategy.in_config_dir("config.toml");
|
||||
//!
|
||||
//! assert_eq!(config_dir.join("config.toml"), config_file);
|
||||
//! ```
|
||||
|
||||
#![warn(missing_docs, rust_2018_idioms, missing_debug_implementations)]
|
||||
|
||||
pub mod app_strategy;
|
||||
pub mod base_strategy;
|
||||
|
||||
pub use app_strategy::{choose_app_strategy, AppStrategy, AppStrategyArgs};
|
||||
pub use base_strategy::{choose_base_strategy, BaseStrategy};
|
||||
|
||||
/// A convenience function that wraps the [`home_dir`](https://docs.rs/home/0.5.4/home/fn.home_dir.html) function from the [home](https://docs.rs/home) crate.
|
||||
pub fn home_dir() -> Result<std::path::PathBuf, HomeDirError> {
|
||||
home::home_dir().ok_or(HomeDirError)
|
||||
}
|
||||
|
||||
/// This error occurs when the home directory cannot be located.
|
||||
#[derive(Debug)]
|
||||
pub struct HomeDirError;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue