1use std::{env, iter::once, path::PathBuf};
4
5const XDG_DATA_HOME: &str = "XDG_DATA_HOME";
6const XDG_DATA_DIRS: &str = "XDG_DATA_DIRS";
7
8pub fn home() -> PathBuf {
13 #[cfg(unix)]
14 let home = env::var("HOME").expect("$HOME must always be set");
15
16 #[cfg(windows)]
17 let home = env::var("USERPROFILE").expect("$USERPROFILE must always be set");
18
19 PathBuf::from(home)
20}
21
22pub fn xdg_config_home() -> PathBuf {
24 if let Ok(path) = env::var("XDG_CONFIG_HOME") {
25 return path.into();
26 }
27
28 home().join(".config")
29}
30
31pub fn xdg_data_home() -> PathBuf {
33 if let Ok(path) = env::var(XDG_DATA_HOME) {
34 return path.into();
35 }
36
37 home().join(".local/share")
38}
39
40pub fn xdg_data_dirs() -> Vec<PathBuf> {
42 if let Ok(path) = env::var(XDG_DATA_DIRS) {
43 return path.split(':').map(PathBuf::from).collect();
44 }
45
46 #[cfg(unix)]
47 return vec![PathBuf::from("/usr/local/share/"), PathBuf::from("/usr/share/")];
48
49 #[cfg(windows)]
50 return vec![];
51}
52
53pub fn xdg_data_globs(path: &str) -> impl Iterator<Item = PathBuf> + '_ {
55 once(xdg_data_home()).chain(xdg_data_dirs()).map(move |mut p| {
56 p.push(path);
57 p
58 })
59}
60
61pub fn xdg_state_home() -> PathBuf {
63 if let Ok(path) = env::var("XDG_STATE_HOME") {
64 return path.into();
65 }
66
67 home().join(".local/state")
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use rstest::rstest;
74
75 #[rstest]
76 pub fn should_return_globs() {
77 unsafe {
78 env::set_var(XDG_DATA_HOME, "/home/user/.xdg/share");
80 env::set_var(XDG_DATA_DIRS, "/share:/data");
81 }
82
83 let paths = xdg_data_globs("test/*").collect::<Vec<_>>();
84
85 assert_eq!(
86 paths,
87 vec![
88 PathBuf::from("/home/user/.xdg/share/test/*"),
89 PathBuf::from("/share/test/*"),
90 PathBuf::from("/data/test/*")
91 ]
92 );
93 }
94}