1use std::path::{Path, PathBuf};
2
3use anyhow::{Context, Result};
4
5pub fn normalize_path(path: &Path) -> Result<PathBuf> {
6 let path_str = path.to_str().context("Invalid path")?;
7 let expanded = shellexpand::full(path_str)?;
8 Ok(PathBuf::from(expanded.as_ref()).canonicalize()?)
9}
10
11#[cfg(test)]
12mod tests {
13 use std::{env, fs};
14
15 use testresult::TestResult;
16
17 use super::*;
18
19 #[test]
20 fn test_normalize_path() -> TestResult {
21 let temp_dir = tempfile::tempdir()?;
22 let home = temp_dir.path();
23 env::set_var("HOME", home.to_str().unwrap());
24
25 let test_path = home.join("path");
26 fs::create_dir_all(&test_path)?;
27
28 let test_cases = vec![
29 (home.to_path_buf(), Ok(home.canonicalize()?)),
31 (PathBuf::from("$HOME/path"), Ok(test_path.canonicalize()?)),
33 (PathBuf::from("${HOME}/path"), Ok(test_path.canonicalize()?)),
34 (PathBuf::from("~/test"), Err(())),
36 (PathBuf::from("\0invalid"), Err(())),
38 ];
39
40 for (input, expected) in test_cases {
41 let result = normalize_path(&input);
42 match expected {
43 Ok(expected_path) => {
44 assert!(result.is_ok());
45 assert_eq!(result.unwrap(), expected_path);
46 }
47 Err(_) => {
48 assert!(result.is_err());
49 }
50 }
51 }
52
53 env::remove_var("HOME");
54 Ok(())
55 }
56}