use std::path::{Path, PathBuf};
pub fn canonicalize_plain(path: impl AsRef<Path>) -> std::io::Result<PathBuf> {
std::fs::canonicalize(path).map(plain_path)
}
pub fn plain_path(path: PathBuf) -> PathBuf {
match path.to_str().and_then(strip_verbatim_drive) {
Some(stripped) => PathBuf::from(stripped),
None => path,
}
}
fn strip_verbatim_drive(s: &str) -> Option<&str> {
let rest = s.strip_prefix(r"\\?\")?;
let b = rest.as_bytes();
let drive = b.len() >= 2 && b[0].is_ascii_alphabetic() && b[1] == b':';
let bounded = b.len() == 2 || b[2] == b'\\';
(drive && bounded).then_some(rest)
}
#[cfg(test)]
mod tests {
use super::*;
fn p(s: &str) -> PathBuf {
plain_path(PathBuf::from(s))
}
#[test]
fn strips_verbatim_drive() {
assert_eq!(
p(r"\\?\C:\Users\a\b.txt"),
PathBuf::from(r"C:\Users\a\b.txt")
);
assert_eq!(p(r"\\?\d:\"), PathBuf::from(r"d:\"));
assert_eq!(p(r"\\?\C:"), PathBuf::from("C:"));
}
#[test]
fn leaves_unc_device_and_plain_paths() {
for s in [
r"\\?\UNC\server\share\x",
r"\\?\Volume{1234}\x",
r"\\.\COM1",
r"\\?\C:foo",
r"\\?\1:\x",
r"C:\Users\a",
"/tmp/x",
"",
] {
assert_eq!(p(s), PathBuf::from(s), "{s}");
}
}
#[test]
fn canonicalize_plain_resolves_existing_and_reports_missing() {
let dir = std::env::temp_dir();
let got = canonicalize_plain(&dir).expect("temp dir exists");
assert!(got.is_absolute());
assert!(
!got.to_string_lossy().starts_with(r"\\?\")
|| got.to_string_lossy().starts_with(r"\\?\UNC")
);
assert!(canonicalize_plain(dir.join("rightkit-fs-no-such-path-272")).is_err());
}
}