1use std::path::{Path, PathBuf};
4
5#[cfg(not(windows))]
7pub fn home_dir() -> Option<PathBuf> {
8 std::env::var_os("HOME").map(PathBuf::from)
9}
10
11#[cfg(windows)]
13pub fn home_dir() -> Option<PathBuf> {
14 std::env::home_dir()
15}
16
17#[cfg(windows)]
19pub fn local_data_dir() -> Option<PathBuf> {
20 std::env::var_os("LOCALAPPDATA").filter(|d| !d.is_empty()).map(PathBuf::from).or_else(|| Some(home_dir()?.join("AppData").join("Local")))
21}
22
23pub fn shown(path: &Path) -> PathBuf {
26 let text = path.as_os_str().to_string_lossy();
27 if let Some(unc) = text.strip_prefix(r"\\?\UNC\") {
28 PathBuf::from(format!(r"\\{unc}"))
29 } else if let Some(disk) = text.strip_prefix(r"\\?\").filter(|p| p.as_bytes().get(1) == Some(&b':')) {
30 PathBuf::from(disk)
31 } else {
32 path.to_path_buf()
33 }
34}
35
36#[cfg(test)]
37pub(crate) mod tests {
38 #[test]
39 fn verbatim_prefixes_are_dropped_for_showing() {
40 use super::shown;
41 use std::path::Path;
42 assert_eq!(shown(Path::new("/home/me/a b")), Path::new("/home/me/a b"));
43 assert_eq!(shown(Path::new(r"C:\me")), Path::new(r"C:\me"));
44 assert_eq!(shown(Path::new(r"\\?\C:\me\a")), Path::new(r"C:\me\a"));
45 assert_eq!(shown(Path::new(r"\\?\UNC\host\share\a")), Path::new(r"\\host\share\a"));
46 assert_eq!(shown(Path::new(r"\\?\Volume{1}\a")), Path::new(r"\\?\Volume{1}\a"));
47 }
48
49 #[cfg(windows)]
52 #[test]
53 fn the_windows_home_folder_is_the_user_profile() {
54 assert_eq!(super::home_dir().map(std::path::PathBuf::into_os_string), std::env::var_os("USERPROFILE"));
55 }
56
57 #[cfg(windows)]
59 pub(crate) fn junction(target: &std::path::Path, link: &std::path::Path) {
60 let made = std::process::Command::new("cmd").arg("/C").arg("mklink").arg("/J").arg(super::shown(link)).arg(super::shown(target)).output().unwrap();
61 assert!(made.status.success(), "{made:?}");
62 }
63}