use std::path::{Component, Path, Prefix};
pub(crate) fn canonical_path_string(path: &Path) -> String {
let mut rendered = String::new();
for component in path.components() {
match component {
Component::Prefix(prefix) => {
let text = match prefix.kind() {
Prefix::VerbatimDisk(letter) => format!("{}:", char::from(letter)),
Prefix::VerbatimUNC(server, share) => {
format!("//{}/{}", server.to_string_lossy(), share.to_string_lossy())
}
Prefix::Verbatim(name) => name.to_string_lossy().into_owned(),
_ => prefix.as_os_str().to_string_lossy().replace('\\', "/"),
};
rendered.push_str(&text);
}
Component::RootDir => {
if !rendered.ends_with('/') {
rendered.push('/');
}
}
other => {
if !rendered.is_empty() && !rendered.ends_with('/') {
rendered.push('/');
}
rendered.push_str(&other.as_os_str().to_string_lossy());
}
}
}
rendered
}
#[cfg(test)]
mod tests {
use super::canonical_path_string;
use std::path::Path;
#[cfg(unix)]
#[test]
fn unix_backslash_remains_a_filename_character() {
assert_eq!(canonical_path_string(Path::new(r"/a/b\c")), r"/a/b\c");
}
#[cfg(windows)]
#[test]
fn plain_and_verbatim_disk_paths_share_one_identity_spelling() {
assert_eq!(
canonical_path_string(Path::new(r"C:\Users\runner\project")),
canonical_path_string(Path::new(r"\\?\C:\Users\runner\project")),
);
assert_eq!(
canonical_path_string(Path::new(r"\\?\C:\Users\runner\project")),
"C:/Users/runner/project"
);
}
}