use crate::EnvSource;
use std::borrow::Cow;
use std::path::PathBuf;
fn home_dir_with_env<E: EnvSource + ?Sized>(env: &E) -> Option<PathBuf> {
if let Some(home) = env.var("HOME").filter(|h| !h.is_empty()) {
return Some(PathBuf::from(home));
}
env.var("USERPROFILE")
.filter(|h| !h.is_empty())
.map(PathBuf::from)
}
pub fn probe_dir() -> PathBuf {
std::env::temp_dir()
}
pub fn expand_tilde(path: &str) -> Cow<'_, str> {
expand_tilde_with_env(path, &crate::ProcessEnvSource)
}
pub fn expand_tilde_with_env<'p, E: EnvSource + ?Sized>(path: &'p str, env: &E) -> Cow<'p, str> {
if let Some(rest) = path.strip_prefix('~')
&& (rest.is_empty() || rest.starts_with('/'))
&& let Some(home) = home_dir_with_env(env)
{
let rest_trimmed = rest.strip_prefix('/').unwrap_or(rest);
let resolved = if rest_trimmed.is_empty() {
home
} else {
home.join(rest_trimmed)
};
return Cow::Owned(resolved.to_string_lossy().into_owned());
}
Cow::Borrowed(path)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::MapEnvSource;
#[test]
fn expands_leading_tilde_slash() {
let env = MapEnvSource::new().with("HOME", "/home/tester");
let expected = PathBuf::from("/home/tester")
.join("x")
.to_string_lossy()
.into_owned();
assert_eq!(expand_tilde_with_env("~/x", &env), expected);
}
#[test]
fn expands_bare_tilde() {
let env = MapEnvSource::new().with("HOME", "/home/tester");
assert_eq!(expand_tilde_with_env("~", &env), "/home/tester");
}
#[test]
fn passes_through_non_tilde_path() {
let env = MapEnvSource::new().with("HOME", "/home/tester");
assert_eq!(
expand_tilde_with_env("/etc/anodize.yaml", &env),
"/etc/anodize.yaml"
);
assert_eq!(
expand_tilde_with_env("./safe~backup.yaml", &env),
"./safe~backup.yaml"
);
}
#[test]
fn user_home_form_not_expanded() {
let env = MapEnvSource::new().with("HOME", "/home/tester");
assert_eq!(expand_tilde_with_env("~bob/foo", &env), "~bob/foo");
assert_eq!(expand_tilde_with_env("~bob", &env), "~bob");
}
#[test]
fn falls_back_to_userprofile() {
let env = MapEnvSource::new().with("USERPROFILE", "/Users/winuser");
let got = expand_tilde_with_env("~/docs", &env).into_owned();
let expected = PathBuf::from("/Users/winuser")
.join("docs")
.to_string_lossy()
.into_owned();
assert_eq!(got, expected);
}
}