use std::path::PathBuf;
pub fn home_dir() -> Option<PathBuf> {
atomcode_core::tool::real_home_dir()
}
pub fn collapse_home(path: &str) -> String {
collapse_home_with(path, home_dir().as_deref())
}
fn collapse_home_with(path: &str, home: Option<&std::path::Path>) -> String {
let path: std::borrow::Cow<'_, str> = if let Some(rest) = path.strip_prefix(r"\\?\UNC\") {
std::borrow::Cow::Owned(format!(r"\\{}", rest))
} else if let Some(rest) = path.strip_prefix(r"\\?\") {
std::borrow::Cow::Borrowed(rest)
} else {
std::borrow::Cow::Borrowed(path)
};
if let Some(home) = home {
let home_str = home.to_string_lossy();
if !home_str.is_empty() {
let rest = if cfg!(windows) {
if path.to_lowercase().starts_with(&home_str.to_lowercase()) {
Some(path[home_str.len()..].to_string())
} else {
None
}
} else {
path.strip_prefix(&*home_str).map(|s| s.to_string())
};
if let Some(rest) = rest {
if rest.is_empty() {
return "~".to_string();
}
return format!("~{}", rest.replace('\\', "/"));
}
}
}
path.into_owned()
}
pub fn history_path() -> PathBuf {
atomcode_core::config::Config::config_dir().join("history")
}
pub fn image_cache_dir() -> PathBuf {
atomcode_core::config::Config::config_dir().join("image-cache")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn collapse_home_rewrites_prefix() {
if let Some(home) = home_dir() {
let home_str = home.to_string_lossy().to_string();
let nested = format!("{}/project/foo", home_str);
let got = collapse_home(&nested);
assert_eq!(got, "~/project/foo");
}
}
#[test]
fn collapse_home_emits_forward_slash_on_windows_separators() {
if let Some(home) = home_dir() {
let home_str = home.to_string_lossy().to_string();
let sep = if home_str.contains('\\') { '\\' } else { '/' };
let nested = format!("{home_str}{sep}atomcode{sep}src");
let got = collapse_home(&nested);
assert_eq!(got, "~/atomcode/src");
assert!(!got.contains('\\'), "must not retain backslashes: {got}");
}
}
#[test]
fn collapse_home_returns_unchanged_for_unrelated_path() {
assert_eq!(collapse_home("/opt/tool/bar"), "/opt/tool/bar");
}
#[test]
fn collapse_home_strips_windows_verbatim_prefix() {
assert_eq!(
collapse_home(r"\\?\D:\wwwroot\xingyu-api"),
r"D:\wwwroot\xingyu-api"
);
}
#[test]
fn collapse_home_strips_windows_verbatim_unc_prefix() {
assert_eq!(
collapse_home(r"\\?\UNC\server\share\proj"),
r"\\server\share\proj"
);
}
#[test]
fn collapse_home_windows_case_insensitive_match() {
let home = std::path::Path::new(r"C:\Users\username");
assert_eq!(
collapse_home_with(r"C:\Users\username\atomcode", Some(home)),
"~/atomcode"
);
if cfg!(windows) {
assert_eq!(
collapse_home_with(r"C:\USERS\username\atomcode", Some(home)),
"~/atomcode"
);
}
}
#[test]
fn collapse_home_windows_verbatim_with_different_case() {
let home = std::path::Path::new(r"C:\Users\username");
assert_eq!(
collapse_home_with(r"\\?\C:\Users\username\atomcode", Some(home)),
"~/atomcode"
);
if cfg!(windows) {
assert_eq!(
collapse_home_with(r"\\?\C:\USERS\username\atomcode", Some(home)),
"~/atomcode"
);
}
}
#[test]
fn collapse_home_windows_exact_home() {
let home = std::path::Path::new(r"C:\Users\username");
assert_eq!(
collapse_home_with(r"C:\Users\username", Some(home)),
"~"
);
}
#[test]
fn collapse_home_windows_deeply_nested_path() {
let home = std::path::Path::new(r"C:\Users\hao");
assert_eq!(
collapse_home_with(
r"C:\Users\hao\Documents\WPSDrive\NotLoginPage",
Some(home),
),
"~/Documents/WPSDrive/NotLoginPage"
);
}
#[test]
fn collapse_home_windows_verbatim_deeply_nested_path() {
let home = std::path::Path::new(r"C:\Users\hao");
assert_eq!(
collapse_home_with(
r"\\?\C:\Users\hao\Documents\WPSDrive\NotLoginPage",
Some(home),
),
"~/Documents/WPSDrive/NotLoginPage"
);
}
#[test]
fn history_path_never_panics() {
let _ = history_path();
}
#[test]
fn image_cache_dir_lives_under_config_dir() {
let p = image_cache_dir();
let cfg = atomcode_core::config::Config::config_dir();
assert!(p.starts_with(&cfg), "{:?} should be under {:?}", p, cfg);
assert_eq!(p.file_name().and_then(|s| s.to_str()), Some("image-cache"));
}
}