use anyhow::{Context, Result};
use std::path::{Component, Path, PathBuf};
pub fn normalize(raw: &str) -> Result<String> {
let expanded = expand_tilde(raw)?;
let expanded = expand_env(&expanded);
Ok(clean(&expanded))
}
fn expand_tilde(path: &str) -> Result<String> {
if !path.starts_with('~') {
return Ok(path.to_string());
}
let home = dirs::home_dir().context("Could not determine home directory")?;
Ok(format!("{}{}", home.display(), &path[1..]))
}
fn expand_env(input: &str) -> String {
let mut result = input.to_string();
while let Some(start) = result.find("${") {
let rest = &result[start + 2..];
match rest.find('}') {
Some(end) => {
let key = &rest[..end];
let value = std::env::var(key).unwrap_or_default();
result = format!("{}{}{}", &result[..start], value, &rest[end + 1..]);
}
None => break,
}
}
result
}
fn clean(path: &str) -> String {
let mut components = Vec::new();
for component in Path::new(path).components() {
match component {
Component::ParentDir => {
components.pop();
}
Component::CurDir => {}
c => components.push(c),
}
}
let result: PathBuf = components.into_iter().collect();
result.to_string_lossy().to_string()
}