Skip to main content

doing_config/
paths.rs

1use std::path::{Path, PathBuf};
2
3use doing_error::{Error, Result};
4
5/// Expands a leading `~` in a path to the user's home directory.
6///
7/// Returns the path unchanged if it does not start with `~`.
8/// Returns an error if `$HOME` cannot be resolved.
9pub fn expand_tilde(path: &Path) -> Result<PathBuf> {
10  let s = path.to_string_lossy();
11  if let Some(rest) = s.strip_prefix("~/") {
12    let home = dir_spec::home().ok_or_else(|| Error::Config("$HOME is not set".into()))?;
13    Ok(home.join(rest))
14  } else if s == "~" {
15    dir_spec::home().ok_or_else(|| Error::Config("$HOME is not set".into()))
16  } else {
17    Ok(path.to_path_buf())
18  }
19}
20
21#[cfg(test)]
22mod test {
23  use super::*;
24
25  mod expand_tilde {
26    use super::*;
27
28    #[test]
29    fn it_expands_bare_tilde() {
30      let result = expand_tilde(Path::new("~")).unwrap();
31
32      assert!(result.is_absolute());
33      assert!(!result.to_string_lossy().contains('~'));
34    }
35
36    #[test]
37    fn it_expands_tilde_prefix() {
38      let result = expand_tilde(Path::new("~/Documents/file.txt")).unwrap();
39
40      assert!(result.is_absolute());
41      assert!(result.ends_with("Documents/file.txt"));
42    }
43
44    #[test]
45    fn it_leaves_absolute_paths_unchanged() {
46      let path = Path::new("/usr/local/bin");
47      let result = expand_tilde(path).unwrap();
48
49      assert_eq!(result, path);
50    }
51
52    #[test]
53    fn it_leaves_relative_paths_unchanged() {
54      let path = Path::new("relative/path");
55      let result = expand_tilde(path).unwrap();
56
57      assert_eq!(result, path);
58    }
59  }
60}