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}