etcetera/app_strategy/
xdg.rs

1use crate::base_strategy::BaseStrategy;
2use crate::{HomeDirError, base_strategy};
3use std::path::{Path, PathBuf};
4
5/// 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.
6///
7/// This initial example removes all the XDG environment variables to show the strategy’s use of the XDG default directories.
8///
9/// ```
10/// use etcetera::app_strategy::AppStrategy;
11/// use etcetera::app_strategy::AppStrategyArgs;
12/// use etcetera::app_strategy::Xdg;
13/// use std::path::Path;
14///
15/// // Remove the environment variables that the strategy reads from.
16/// unsafe {
17/// std::env::remove_var("XDG_CONFIG_HOME");
18/// std::env::remove_var("XDG_DATA_HOME");
19/// std::env::remove_var("XDG_CACHE_HOME");
20/// std::env::remove_var("XDG_STATE_HOME");
21/// std::env::remove_var("XDG_RUNTIME_DIR");
22/// }
23///
24/// let app_strategy = Xdg::new(AppStrategyArgs {
25///     top_level_domain: "org".to_string(),
26///     author: "Acme Corp".to_string(),
27///     app_name: "Frobnicator Plus".to_string(),
28/// }).unwrap();
29///
30/// let home_dir = etcetera::home_dir().unwrap();
31///
32/// assert_eq!(
33///     app_strategy.home_dir(),
34///     &home_dir
35/// );
36/// assert_eq!(
37///     app_strategy.config_dir().strip_prefix(&home_dir),
38///     Ok(Path::new(".config/frobnicator-plus/"))
39/// );
40/// assert_eq!(
41///     app_strategy.data_dir().strip_prefix(&home_dir),
42///     Ok(Path::new(".local/share/frobnicator-plus/"))
43/// );
44/// assert_eq!(
45///     app_strategy.cache_dir().strip_prefix(&home_dir),
46///     Ok(Path::new(".cache/frobnicator-plus/"))
47/// );
48/// assert_eq!(
49///     app_strategy.state_dir().unwrap().strip_prefix(&home_dir),
50///     Ok(Path::new(".local/state/frobnicator-plus/"))
51/// );
52/// assert_eq!(
53///     app_strategy.runtime_dir(),
54///     None
55/// );
56/// ```
57///
58/// This next example gives the environment variables values:
59///
60/// ```
61/// use etcetera::app_strategy::AppStrategy;
62/// use etcetera::app_strategy::AppStrategyArgs;
63/// use etcetera::app_strategy::Xdg;
64/// use std::path::Path;
65///
66/// // We need to conditionally set these to ensure that they are absolute paths both on Windows and other systems.
67/// let config_path = if cfg!(windows) {
68///     "C:\\my_config_location\\"
69/// } else {
70///     "/my_config_location/"
71/// };
72/// let data_path = if cfg!(windows) {
73///     "C:\\my_data_location\\"
74/// } else {
75///     "/my_data_location/"
76/// };
77/// let cache_path = if cfg!(windows) {
78///     "C:\\my_cache_location\\"
79/// } else {
80///     "/my_cache_location/"
81/// };
82/// let state_path = if cfg!(windows) {
83///     "C:\\my_state_location\\"
84/// } else {
85///     "/my_state_location/"
86/// };
87/// let runtime_path = if cfg!(windows) {
88///     "C:\\my_runtime_location\\"
89/// } else {
90///     "/my_runtime_location/"
91/// };
92///
93/// unsafe {
94/// std::env::set_var("XDG_CONFIG_HOME", config_path);
95/// std::env::set_var("XDG_DATA_HOME", data_path);
96/// std::env::set_var("XDG_CACHE_HOME", cache_path);
97/// std::env::set_var("XDG_STATE_HOME", state_path);
98/// std::env::set_var("XDG_RUNTIME_DIR", runtime_path);
99/// }
100///
101/// let app_strategy = Xdg::new(AppStrategyArgs {
102///     top_level_domain: "org".to_string(),
103///     author: "Acme Corp".to_string(),
104///     app_name: "Frobnicator Plus".to_string(),
105/// }).unwrap();
106///
107/// assert_eq!(
108///     app_strategy.config_dir(),
109///     Path::new(&format!("{}/frobnicator-plus/", config_path))
110/// );
111/// assert_eq!(
112///     app_strategy.data_dir(),
113///     Path::new(&format!("{}/frobnicator-plus/", data_path))
114/// );
115/// assert_eq!(
116///     app_strategy.cache_dir(),
117///     Path::new(&format!("{}/frobnicator-plus/", cache_path))
118/// );
119/// assert_eq!(
120///     app_strategy.state_dir().unwrap(),
121///     Path::new(&format!("{}/frobnicator-plus/", state_path))
122/// );
123/// assert_eq!(
124///     app_strategy.runtime_dir().unwrap(),
125///     Path::new(&format!("{}/frobnicator-plus/", runtime_path))
126/// );
127/// ```
128///
129/// The XDG spec requires that when the environment variables’ values are not absolute paths, their values should be ignored. This example exemplifies this behaviour:
130///
131/// ```
132/// use etcetera::app_strategy::AppStrategy;
133/// use etcetera::app_strategy::AppStrategyArgs;
134/// use etcetera::app_strategy::Xdg;
135/// use std::path::Path;
136///
137/// // Remove the environment variables that the strategy reads from.
138/// unsafe {
139/// std::env::set_var("XDG_CONFIG_HOME", "relative_path/");
140/// std::env::set_var("XDG_DATA_HOME", "./another_one/");
141/// std::env::set_var("XDG_CACHE_HOME", "yet_another/");
142/// std::env::set_var("XDG_STATE_HOME", "./and_another");
143/// std::env::set_var("XDG_RUNTIME_DIR", "relative_path/");
144/// }
145///
146/// let app_strategy = Xdg::new(AppStrategyArgs {
147///     top_level_domain: "org".to_string(),
148///     author: "Acme Corp".to_string(),
149///     app_name: "Frobnicator Plus".to_string(),
150/// }).unwrap();
151///
152/// let home_dir = etcetera::home_dir().unwrap();
153///
154/// // We still get the default values.
155/// assert_eq!(
156///     app_strategy.config_dir().strip_prefix(&home_dir),
157///     Ok(Path::new(".config/frobnicator-plus/"))
158/// );
159/// assert_eq!(
160///     app_strategy.data_dir().strip_prefix(&home_dir),
161///     Ok(Path::new(".local/share/frobnicator-plus/"))
162/// );
163/// assert_eq!(
164///     app_strategy.cache_dir().strip_prefix(&home_dir),
165///     Ok(Path::new(".cache/frobnicator-plus/"))
166/// );
167/// assert_eq!(
168///     app_strategy.state_dir().unwrap().strip_prefix(&home_dir),
169///     Ok(Path::new(".local/state/frobnicator-plus/"))
170/// );
171/// assert_eq!(
172///     app_strategy.runtime_dir(),
173///     None
174/// );
175/// ```
176#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
177pub struct Xdg {
178    base_strategy: base_strategy::Xdg,
179    unixy_name: String,
180}
181
182impl Xdg {
183    /// Create a new Xdg AppStrategy
184    pub fn new(args: super::AppStrategyArgs) -> Result<Self, HomeDirError> {
185        Ok(Self {
186            base_strategy: base_strategy::Xdg::new()?,
187            unixy_name: args.unixy_name(),
188        })
189    }
190}
191
192impl super::AppStrategy for Xdg {
193    fn home_dir(&self) -> &Path {
194        self.base_strategy.home_dir()
195    }
196
197    fn config_dir(&self) -> PathBuf {
198        self.base_strategy.config_dir().join(&self.unixy_name)
199    }
200
201    fn data_dir(&self) -> PathBuf {
202        self.base_strategy.data_dir().join(&self.unixy_name)
203    }
204
205    fn cache_dir(&self) -> PathBuf {
206        self.base_strategy.cache_dir().join(&self.unixy_name)
207    }
208
209    fn state_dir(&self) -> Option<PathBuf> {
210        Some(
211            self.base_strategy
212                .state_dir()
213                .unwrap()
214                .join(&self.unixy_name),
215        )
216    }
217
218    fn runtime_dir(&self) -> Option<PathBuf> {
219        self.base_strategy
220            .runtime_dir()
221            .map(|runtime_dir| runtime_dir.join(&self.unixy_name))
222    }
223}