Skip to main content

bhtune_runtime/config/
paths.rs

1#![allow(rustdoc::broken_intra_doc_links)]
2
3use std::path::{Path, PathBuf};
4
5/// Derive bhtune's config file location from raw environment values rather than reading
6/// `std::env` directly -- keeps discovery fully unit-testable across every permutation
7/// without mutating real process environment variables.
8///
9/// - Windows (`is_windows = true`): `%APPDATA%\bhtune\bhtune.toml`.
10/// - Elsewhere: `$XDG_CONFIG_HOME/bhtune/bhtune.toml`, falling back to
11///   `$HOME/.config/bhtune/bhtune.toml`.
12pub fn config_path_from(
13    xdg_config_home: Option<&str>,
14    home: Option<&str>,
15    appdata: Option<&str>,
16    is_windows: bool,
17) -> Option<PathBuf> {
18    if is_windows {
19        return appdata.map(|dir| Path::new(dir).join("bhtune").join("bhtune.toml"));
20    }
21    if let Some(dir) = xdg_config_home {
22        return Some(Path::new(dir).join("bhtune").join("bhtune.toml"));
23    }
24    home.map(|dir| {
25        Path::new(dir)
26            .join(".config")
27            .join("bhtune")
28            .join("bhtune.toml")
29    })
30}
31
32/// Derive bhtune's default *user template catalog* location the same way [`config_path_from`]
33/// derives `bhtune.toml`'s -- deliberately the same directory, since both are per-user
34/// settings a site admin edits by hand, not persistent application data (contrast
35/// [`default_db_path_from`]/[`default_log_dir_from`], which live under the platform data
36/// directory instead). See [`load_user_templates`] for how this default fits into the full
37/// configuration precedence chain.
38///
39/// - Windows (`is_windows = true`): `%APPDATA%\bhtune\templates.toml`.
40/// - Elsewhere: `$XDG_CONFIG_HOME/bhtune/templates.toml`, falling back to
41///   `$HOME/.config/bhtune/templates.toml`.
42pub fn templates_path_from(
43    xdg_config_home: Option<&str>,
44    home: Option<&str>,
45    appdata: Option<&str>,
46    is_windows: bool,
47) -> Option<PathBuf> {
48    if is_windows {
49        return appdata.map(|dir| Path::new(dir).join("bhtune").join("templates.toml"));
50    }
51    if let Some(dir) = xdg_config_home {
52        return Some(Path::new(dir).join("bhtune").join("templates.toml"));
53    }
54    home.map(|dir| {
55        Path::new(dir)
56            .join(".config")
57            .join("bhtune")
58            .join("templates.toml")
59    })
60}
61
62/// Derive bhtune's default *database* location the same way config files are discovered, but
63/// under the platform's data directory rather than its config directory -- a database is
64/// persistent user data, not settings, per the XDG base directory specification.
65///
66/// - Windows (`is_windows = true`): `%APPDATA%\bhtune\bhtune.db`.
67/// - Elsewhere: `$XDG_DATA_HOME/bhtune/bhtune.db`, falling back to
68///   `$HOME/.local/share/bhtune/bhtune.db`.
69/// - A database location must always resolve to *something* usable (unlike the config
70///   file, whose absence is fine) -- if none of the above are available, this falls back
71///   further to `bhtune.db` in the current directory, matching the CLI's original
72///   hardcoded placeholder default.
73pub fn default_db_path_from(
74    xdg_data_home: Option<&str>,
75    home: Option<&str>,
76    appdata: Option<&str>,
77    is_windows: bool,
78) -> PathBuf {
79    if is_windows {
80        return appdata
81            .map(|dir| Path::new(dir).join("bhtune").join("bhtune.db"))
82            .unwrap_or_else(|| PathBuf::from("bhtune.db"));
83    }
84    if let Some(dir) = xdg_data_home {
85        return Path::new(dir).join("bhtune").join("bhtune.db");
86    }
87    home.map(|dir| {
88        Path::new(dir)
89            .join(".local")
90            .join("share")
91            .join("bhtune")
92            .join("bhtune.db")
93    })
94    .unwrap_or_else(|| PathBuf::from("bhtune.db"))
95}
96
97/// Derive bhtune's default *log directory* the same way the database path is derived (see
98/// [`default_db_path_from`]) -- under the platform data directory, not next to the compiled
99/// binary. Unlike `opcda-bridge-gateway`'s equivalent (`log_dir_from_exe`), a `cargo
100/// install`ed binary's own directory (e.g. `~/.cargo/bin/`) isn't a sensible place to write
101/// logs, and bhtune already has this exact precedence machinery for the database, so the log
102/// directory reuses it rather than inventing a second convention.
103///
104/// - Windows (`is_windows = true`): `%APPDATA%\bhtune\logs`.
105/// - Elsewhere: `$XDG_DATA_HOME/bhtune/logs`, falling back to `$HOME/.local/share/bhtune/logs`.
106/// - Falls back further to `logs` in the current directory if none of the above are
107///   available, matching [`default_db_path_from`]'s own final fallback.
108pub fn default_log_dir_from(
109    xdg_data_home: Option<&str>,
110    home: Option<&str>,
111    appdata: Option<&str>,
112    is_windows: bool,
113) -> PathBuf {
114    if is_windows {
115        return appdata
116            .map(|dir| Path::new(dir).join("bhtune").join("logs"))
117            .unwrap_or_else(|| PathBuf::from("logs"));
118    }
119    if let Some(dir) = xdg_data_home {
120        return Path::new(dir).join("bhtune").join("logs");
121    }
122    home.map(|dir| {
123        Path::new(dir)
124            .join(".local")
125            .join("share")
126            .join("bhtune")
127            .join("logs")
128    })
129    .unwrap_or_else(|| PathBuf::from("logs"))
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135    #[test]
136    fn config_path_from_windows_with_appdata() {
137        let path = config_path_from(None, None, Some(r"C:\Users\me\AppData\Roaming"), true);
138        assert_eq!(
139            path,
140            Some(PathBuf::from(
141                r"C:\Users\me\AppData\Roaming/bhtune/bhtune.toml"
142            ))
143        );
144    }
145
146    #[test]
147    fn config_path_from_windows_no_appdata() {
148        assert_eq!(
149            config_path_from(Some("/xdg"), Some("/home"), None, true),
150            None
151        );
152    }
153
154    #[test]
155    fn config_path_from_unix_xdg_config_home() {
156        let path = config_path_from(Some("/xdg"), Some("/home/me"), None, false);
157        assert_eq!(path, Some(PathBuf::from("/xdg/bhtune/bhtune.toml")));
158    }
159
160    #[test]
161    fn config_path_from_unix_falls_back_to_home() {
162        let path = config_path_from(None, Some("/home/me"), None, false);
163        assert_eq!(
164            path,
165            Some(PathBuf::from("/home/me/.config/bhtune/bhtune.toml"))
166        );
167    }
168
169    #[test]
170    fn config_path_from_unix_no_env_vars() {
171        assert_eq!(config_path_from(None, None, None, false), None);
172    }
173
174    #[test]
175    fn config_path_from_unix_xdg_takes_precedence_over_home() {
176        let path = config_path_from(Some("/xdg"), Some("/home/me"), None, false);
177        assert_eq!(path, Some(PathBuf::from("/xdg/bhtune/bhtune.toml")));
178    }
179
180    #[test]
181    fn templates_path_from_windows_with_appdata() {
182        let path = templates_path_from(None, None, Some(r"C:\Users\me\AppData\Roaming"), true);
183        assert_eq!(
184            path,
185            Some(PathBuf::from(
186                r"C:\Users\me\AppData\Roaming/bhtune/templates.toml"
187            ))
188        );
189    }
190
191    #[test]
192    fn templates_path_from_windows_no_appdata() {
193        assert_eq!(
194            templates_path_from(Some("/xdg"), Some("/home"), None, true),
195            None
196        );
197    }
198
199    #[test]
200    fn templates_path_from_unix_xdg_config_home() {
201        let path = templates_path_from(Some("/xdg"), Some("/home/me"), None, false);
202        assert_eq!(path, Some(PathBuf::from("/xdg/bhtune/templates.toml")));
203    }
204
205    #[test]
206    fn templates_path_from_unix_falls_back_to_home() {
207        let path = templates_path_from(None, Some("/home/me"), None, false);
208        assert_eq!(
209            path,
210            Some(PathBuf::from("/home/me/.config/bhtune/templates.toml"))
211        );
212    }
213
214    #[test]
215    fn templates_path_from_unix_no_env_vars() {
216        assert_eq!(templates_path_from(None, None, None, false), None);
217    }
218
219    #[test]
220    fn default_db_path_from_windows_with_appdata() {
221        let path = default_db_path_from(None, None, Some(r"C:\Users\me\AppData\Roaming"), true);
222        assert_eq!(
223            path,
224            PathBuf::from(r"C:\Users\me\AppData\Roaming/bhtune/bhtune.db")
225        );
226    }
227
228    #[test]
229    fn default_db_path_from_windows_no_appdata_falls_back_to_cwd() {
230        assert_eq!(
231            default_db_path_from(None, None, None, true),
232            PathBuf::from("bhtune.db")
233        );
234    }
235
236    #[test]
237    fn default_db_path_from_unix_xdg_data_home() {
238        let path = default_db_path_from(Some("/xdg-data"), Some("/home/me"), None, false);
239        assert_eq!(path, PathBuf::from("/xdg-data/bhtune/bhtune.db"));
240    }
241
242    #[test]
243    fn default_db_path_from_unix_falls_back_to_home() {
244        let path = default_db_path_from(None, Some("/home/me"), None, false);
245        assert_eq!(
246            path,
247            PathBuf::from("/home/me/.local/share/bhtune/bhtune.db")
248        );
249    }
250
251    #[test]
252    fn default_db_path_from_unix_no_env_vars_falls_back_to_cwd() {
253        assert_eq!(
254            default_db_path_from(None, None, None, false),
255            PathBuf::from("bhtune.db")
256        );
257    }
258
259    #[test]
260    fn default_log_dir_from_windows_with_appdata() {
261        let path = default_log_dir_from(None, None, Some(r"C:\Users\me\AppData\Roaming"), true);
262        assert_eq!(
263            path,
264            PathBuf::from(r"C:\Users\me\AppData\Roaming/bhtune/logs")
265        );
266    }
267
268    #[test]
269    fn default_log_dir_from_windows_no_appdata_falls_back_to_cwd() {
270        assert_eq!(
271            default_log_dir_from(None, None, None, true),
272            PathBuf::from("logs")
273        );
274    }
275
276    #[test]
277    fn default_log_dir_from_unix_xdg_data_home() {
278        let path = default_log_dir_from(Some("/xdg-data"), Some("/home/me"), None, false);
279        assert_eq!(path, PathBuf::from("/xdg-data/bhtune/logs"));
280    }
281
282    #[test]
283    fn default_log_dir_from_unix_falls_back_to_home() {
284        let path = default_log_dir_from(None, Some("/home/me"), None, false);
285        assert_eq!(path, PathBuf::from("/home/me/.local/share/bhtune/logs"));
286    }
287
288    #[test]
289    fn default_log_dir_from_unix_no_env_vars_falls_back_to_cwd() {
290        assert_eq!(
291            default_log_dir_from(None, None, None, false),
292            PathBuf::from("logs")
293        );
294    }
295}