Skip to main content

utils/
path.rs

1use std::path::{Path, PathBuf};
2
3/// Renders `path` relative to the user's home directory (`~/…`) when it lives
4/// inside it, mirroring the way shells display paths.
5pub fn home_relative_path(path: &Path) -> String {
6    home_dir().map_or_else(|| path.display().to_string(), |home| home_relative_path_with_home(path, &home))
7}
8
9fn home_dir() -> Option<PathBuf> {
10    std::env::var_os("HOME").or_else(|| std::env::var_os("USERPROFILE")).map(PathBuf::from)
11}
12
13fn home_relative_path_with_home(path: &Path, home: &Path) -> String {
14    if path == home {
15        return "~".to_string();
16    }
17    path.strip_prefix(home)
18        .ok()
19        .filter(|relative| !relative.as_os_str().is_empty())
20        .map_or_else(|| path.display().to_string(), |relative| format!("~/{}", relative.display()))
21}
22
23#[cfg(test)]
24mod tests {
25    use super::*;
26
27    #[test]
28    fn paths_inside_home_shorten_to_tilde() {
29        let home = Path::new("/home/user");
30        assert_eq!(home_relative_path_with_home(&home.join("project"), home), "~/project");
31        assert_eq!(home_relative_path_with_home(home, home), "~");
32    }
33
34    #[test]
35    fn paths_outside_home_stay_absolute() {
36        let home = Path::new("/home/user");
37        assert_eq!(home_relative_path_with_home(Path::new("/etc/config"), home), "/etc/config");
38        assert_eq!(home_relative_path_with_home(home.parent().unwrap(), home), "/home");
39    }
40}