1use std::path::{Path, PathBuf};
2
3use doing_error::{Error, Result};
4
5pub 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}