use std::path::{Path, PathBuf};
const VERBATIM_PREFIX: &str = r"\\?\";
const VERBATIM_UNC_PREFIX: &str = r"\\?\UNC\";
const MAX_PATH: usize = 260;
const RESERVED_DEVICE_NAMES: [&str; 22] = [
"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8",
"COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
];
pub fn display(path: &Path) -> String {
let text = path.to_string_lossy();
plain_form(&text).unwrap_or_else(|| text.into_owned())
}
pub fn elide_front(path: &str, width: usize) -> String {
if width == 0 {
return String::new();
}
if path.chars().count() <= width {
return path.to_owned();
}
let keep = width.saturating_sub(1);
let tail: String = path
.chars()
.skip(path.chars().count().saturating_sub(keep))
.collect();
let snapped = tail
.find(['/', '\\'])
.map(|i| &tail[i..])
.unwrap_or(tail.as_str());
format!("…{snapped}")
}
pub fn simplified(path: &Path) -> PathBuf {
if !cfg!(windows) {
return path.to_owned();
}
match path
.to_str()
.and_then(plain_form)
.filter(|p| is_safe_plain(p))
{
Some(plain) => PathBuf::from(plain),
None => path.to_owned(),
}
}
pub fn canonicalize(path: &Path) -> std::io::Result<PathBuf> {
std::fs::canonicalize(path).map(|resolved| simplified(&resolved))
}
fn plain_form(text: &str) -> Option<String> {
if let Some(rest) = text.strip_prefix(VERBATIM_UNC_PREFIX) {
if rest.is_empty() {
return None;
}
return Some(format!(r"\\{rest}"));
}
let rest = text.strip_prefix(VERBATIM_PREFIX)?;
starts_with_drive(rest).then(|| rest.to_owned())
}
fn starts_with_drive(text: &str) -> bool {
let mut chars = text.chars();
chars.next().is_some_and(|c| c.is_ascii_alphabetic())
&& chars.next() == Some(':')
&& chars.next() == Some('\\')
}
fn is_safe_plain(plain: &str) -> bool {
plain.len() < MAX_PATH && components_after_root(plain).all(is_literal_component)
}
fn components_after_root(plain: &str) -> impl Iterator<Item = &str> {
let body = match plain.strip_prefix(r"\\") {
Some(unc) => unc.splitn(3, '\\').nth(2).unwrap_or(""),
None => plain.get(3..).unwrap_or(""),
};
body.split('\\').filter(|part| !part.is_empty())
}
fn is_literal_component(component: &str) -> bool {
if component == "." || component == ".." {
return false;
}
if component.ends_with('.') || component.ends_with(' ') {
return false;
}
if component.contains(['/', '?', '*', ':', '<', '>', '"', '|']) {
return false;
}
let stem = component.split('.').next().unwrap_or(component);
!RESERVED_DEVICE_NAMES
.iter()
.any(|reserved| stem.eq_ignore_ascii_case(reserved))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_verbatim_drive_path_has_a_plain_form() {
assert_eq!(
plain_form(r"\\?\C:\Users\me\notes.txt").as_deref(),
Some(r"C:\Users\me\notes.txt")
);
}
#[test]
fn a_verbatim_unc_path_folds_back_to_two_backslashes() {
assert_eq!(
plain_form(r"\\?\UNC\host\share\file.txt").as_deref(),
Some(r"\\host\share\file.txt")
);
}
#[test]
fn a_plain_path_has_no_verbatim_form_to_strip() {
assert_eq!(plain_form(r"C:\Users\me"), None);
assert_eq!(plain_form("/home/me/notes.txt"), None);
}
#[test]
fn a_device_path_keeps_its_prefix() {
assert_eq!(
plain_form(r"\\?\Volume{9a1b2c3d-0000-0000-0000-000000000000}\data"),
None
);
assert_eq!(plain_form(r"\\?\pipe\trail"), None);
}
#[test]
fn an_ordinary_path_is_safe_to_simplify() {
assert!(is_safe_plain(r"C:\Users\me\proj\trail\src\pathfmt.rs"));
assert!(is_safe_plain(r"\\host\share\team\notes.txt"));
assert!(is_safe_plain(r"C:\"));
}
#[test]
fn a_path_past_max_path_keeps_the_prefix() {
let long = format!(r"C:\{}", "a".repeat(MAX_PATH));
assert!(!is_safe_plain(&long));
}
#[test]
fn a_reserved_device_name_keeps_the_prefix() {
assert!(!is_safe_plain(r"C:\tmp\nul.txt"));
assert!(!is_safe_plain(r"C:\tmp\CON"));
assert!(!is_safe_plain(r"C:\tmp\com4.log"));
assert!(is_safe_plain(r"C:\tmp\console.log"));
assert!(is_safe_plain(r"C:\tmp\com10"));
}
#[test]
fn a_component_win32_would_rewrite_keeps_the_prefix() {
assert!(!is_safe_plain(r"C:\tmp\report."));
assert!(!is_safe_plain(r"C:\tmp\report "));
assert!(!is_safe_plain(r"C:\tmp\..\other"));
assert!(!is_safe_plain(r"C:\tmp\a/b"));
assert!(!is_safe_plain(r"C:\tmp\a:stream"));
assert!(!is_safe_plain(r"C:\tmp\wild*card"));
}
#[test]
fn a_unc_host_and_share_are_not_checked_as_file_names() {
assert!(is_safe_plain(r"\\host\con$\notes.txt"));
}
#[test]
fn display_drops_the_verbatim_prefix() {
assert_eq!(
display(Path::new(r"\\?\C:\Users\me")),
r"C:\Users\me".to_owned()
);
assert_eq!(
display(Path::new(r"\\?\UNC\host\share")),
r"\\host\share".to_owned()
);
}
#[test]
fn display_leaves_every_other_path_alone() {
assert_eq!(display(Path::new("/home/me")), "/home/me".to_owned());
assert_eq!(
display(Path::new(r"C:\Users\me")),
r"C:\Users\me".to_owned()
);
}
#[test]
fn display_renders_a_long_path_readably() {
let long = format!(r"\\?\C:\{}", "a".repeat(MAX_PATH));
assert!(!display(Path::new(&long)).contains(VERBATIM_PREFIX));
}
#[test]
#[cfg(windows)]
fn simplified_drops_a_prefix_it_can_drop() {
assert_eq!(
simplified(Path::new(r"\\?\C:\Users\me")),
PathBuf::from(r"C:\Users\me")
);
}
#[test]
#[cfg(windows)]
fn simplified_keeps_a_prefix_the_path_needs() {
let long = format!(r"\\?\C:\{}", "a".repeat(MAX_PATH));
assert_eq!(simplified(Path::new(&long)), PathBuf::from(&long));
assert_eq!(
simplified(Path::new(r"\\?\C:\tmp\nul.txt")),
PathBuf::from(r"\\?\C:\tmp\nul.txt")
);
}
#[test]
#[cfg(not(windows))]
fn simplified_is_the_identity_off_windows() {
for path in [r"\\?\C:\Users\me", "/home/me/notes.txt"] {
assert_eq!(simplified(Path::new(path)), PathBuf::from(path));
}
}
#[test]
fn canonicalize_resolves_and_leaves_no_verbatim_prefix() {
let dir = tempfile::tempdir().expect("tempdir");
let resolved = canonicalize(dir.path()).expect("canonicalize");
assert!(resolved.is_absolute());
assert!(
!resolved.to_string_lossy().starts_with(VERBATIM_PREFIX),
"a temp directory is short and ordinary — it should simplify: {resolved:?}"
);
}
#[test]
fn elide_front_leaves_a_path_that_fits() {
assert_eq!(elide_front("/home/me/project", 40), "/home/me/project");
assert_eq!(elide_front("/home/me", 8), "/home/me");
}
#[test]
fn elide_front_drops_the_head_and_keeps_where_you_are() {
let out = elide_front("/home/me/work/deep/project", 14);
assert!(out.starts_with('…'), "the cut must be visible: {out}");
assert!(out.ends_with("project"), "the tail must survive: {out}");
assert!(out.chars().count() <= 14, "over budget: {out}");
}
#[test]
fn elide_front_snaps_to_a_component_boundary() {
assert_eq!(elide_front("/home/me/work/deep", 10), "…/deep");
}
#[test]
fn elide_front_honours_a_windows_separator() {
let out = elide_front(r"C:\Users\weedo\proj\it\util\trail", 16);
assert!(out.starts_with('…'));
assert!(out.ends_with("trail"), "{out}");
assert!(
out.contains('\\'),
"a Windows path should snap on its own separator: {out}"
);
assert!(out.chars().count() <= 16, "over budget: {out}");
}
#[test]
fn elide_front_of_a_component_with_no_separator_still_fits() {
let out = elide_front("averylongsinglecomponent", 10);
assert!(out.starts_with('…'));
assert!(out.chars().count() <= 10, "over budget: {out}");
}
#[test]
fn elide_front_of_zero_width_is_empty() {
assert_eq!(elide_front("/home/me", 0), "");
}
}