etcetera/app_strategy/
windows.rs

1use crate::base_strategy::BaseStrategy;
2use crate::{HomeDirError, base_strategy};
3use std::path::{Path, PathBuf};
4
5/// 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).
6///
7/// This initial example removes all the relevant environment variables to show the strategy’s use of the:
8/// - (on Windows) SHGetKnownFolderPath API.
9/// - (on non-Windows) Windows default directories.
10///
11/// ```
12/// use etcetera::app_strategy::AppStrategy;
13/// use etcetera::app_strategy::AppStrategyArgs;
14/// use etcetera::app_strategy::Windows;
15/// use std::path::Path;
16///
17/// // Remove the environment variables that the strategy reads from.
18/// unsafe {
19/// std::env::remove_var("USERPROFILE");
20/// std::env::remove_var("APPDATA");
21/// std::env::remove_var("LOCALAPPDATA");
22/// }
23///
24/// let app_strategy = Windows::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("AppData/Roaming/Acme Corp/Frobnicator Plus/config"))
39/// );
40/// assert_eq!(
41///     app_strategy.data_dir().strip_prefix(&home_dir),
42///     Ok(Path::new("AppData/Roaming/Acme Corp/Frobnicator Plus/data"))
43/// );
44/// assert_eq!(
45///     app_strategy.cache_dir().strip_prefix(&home_dir),
46///     Ok(Path::new("AppData/Local/Acme Corp/Frobnicator Plus/cache"))
47/// );
48/// assert_eq!(
49///     app_strategy.state_dir(),
50///     None
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::Windows;
64/// use std::path::Path;
65///
66/// let home_path = if cfg!(windows) {
67///     "C:\\my_home_location\\".to_string()
68/// } else {
69///     etcetera::home_dir().unwrap().to_string_lossy().to_string()
70/// };
71/// let data_path = if cfg!(windows) {
72///     "C:\\my_data_location\\"
73/// } else {
74///     "/my_data_location/"
75/// };
76/// let cache_path = if cfg!(windows) {
77///     "C:\\my_cache_location\\"
78/// } else {
79///     "/my_cache_location/"
80/// };
81///
82/// unsafe {
83/// std::env::set_var("USERPROFILE", &home_path);
84/// std::env::set_var("APPDATA", data_path);
85/// std::env::set_var("LOCALAPPDATA", cache_path);
86/// }
87///
88/// let app_strategy = Windows::new(AppStrategyArgs {
89///     top_level_domain: "org".to_string(),
90///     author: "Acme Corp".to_string(),
91///     app_name: "Frobnicator Plus".to_string(),
92/// }).unwrap();
93///
94/// assert_eq!(
95///     app_strategy.home_dir(),
96///     Path::new(&home_path)
97/// );
98/// assert_eq!(
99///     app_strategy.config_dir(),
100///     Path::new(&format!("{}/Acme Corp/Frobnicator Plus/config", data_path))
101/// );
102/// assert_eq!(
103///     app_strategy.data_dir(),
104///     Path::new(&format!("{}/Acme Corp/Frobnicator Plus/data", data_path))
105/// );
106/// assert_eq!(
107///     app_strategy.cache_dir(),
108///     Path::new(&format!("{}/Acme Corp/Frobnicator Plus/cache", cache_path))
109/// );
110/// assert_eq!(
111///     app_strategy.state_dir(),
112///     None
113/// );
114/// assert_eq!(
115///     app_strategy.runtime_dir(),
116///     None
117/// );
118/// ```
119
120#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
121pub struct Windows {
122    base_strategy: base_strategy::Windows,
123    author_app_name_path: PathBuf,
124}
125
126macro_rules! dir_method {
127    ($self: ident, $base_strategy_method: ident, $subfolder_name: expr) => {{
128        let mut path = $self.base_strategy.$base_strategy_method();
129        path.push(&$self.author_app_name_path);
130        path.push($subfolder_name);
131
132        path
133    }};
134}
135
136impl Windows {
137    /// Create a new Windows AppStrategy
138    pub fn new(args: super::AppStrategyArgs) -> Result<Self, HomeDirError> {
139        Ok(Self {
140            base_strategy: base_strategy::Windows::new()?,
141            author_app_name_path: PathBuf::from(args.author).join(args.app_name),
142        })
143    }
144}
145
146impl super::AppStrategy for Windows {
147    fn home_dir(&self) -> &Path {
148        self.base_strategy.home_dir()
149    }
150
151    fn config_dir(&self) -> PathBuf {
152        dir_method!(self, config_dir, "config")
153    }
154
155    fn data_dir(&self) -> PathBuf {
156        dir_method!(self, data_dir, "data")
157    }
158
159    fn cache_dir(&self) -> PathBuf {
160        dir_method!(self, cache_dir, "cache")
161    }
162
163    fn state_dir(&self) -> Option<PathBuf> {
164        None
165    }
166
167    fn runtime_dir(&self) -> Option<PathBuf> {
168        None
169    }
170}