use std::ffi::OsStr;
use std::path::{Component, Path, PathBuf};
pub fn normalize<P: AsRef<OsStr>>(path: P) -> PathBuf {
let path = Path::new(&path);
let mut components = path.components().peekable();
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
components.next();
PathBuf::from(c.as_os_str())
} else {
PathBuf::new()
};
for component in components {
match component {
Component::Prefix(..) => unreachable!(),
Component::RootDir => {
ret.push(Component::RootDir);
}
Component::CurDir => {}
Component::ParentDir => {
if ret.ends_with(Component::ParentDir) {
ret.push(Component::ParentDir);
} else {
let popped = ret.pop();
if !popped && !ret.has_root() {
ret.push(Component::ParentDir);
}
}
}
Component::Normal(c) => {
ret.push(c);
}
}
}
ret
}
fn try_normalize<P: AsRef<OsStr>>(path: P) -> Option<PathBuf> {
let path = Path::new(&path);
let mut ret = PathBuf::new();
for component in path.components() {
match component {
Component::Prefix(..) | Component::RootDir => return None,
Component::CurDir => {}
Component::ParentDir => {
if !ret.pop() {
return None;
}
}
Component::Normal(c) => {
ret.push(c);
}
}
}
Some(ret)
}
pub fn normalize_sep<P: AsRef<OsStr>>(path: P, seq: &str) -> PathBuf {
let path = normalize(path);
let s = path
.into_os_string()
.into_string()
.expect("Failed to convert Path to String");
if s.contains("\\") {
return PathBuf::from(s.replace("\\", seq));
} else if s.contains("/") {
return PathBuf::from(s.replace("/", seq));
}
PathBuf::from(s).to_path_buf()
}
fn is_normalized(path: &Path) -> bool {
for component in path.components() {
match component {
Component::CurDir | Component::ParentDir => {
return false;
}
_ => continue,
}
}
true
}
pub trait NormalizePath {
fn normalize(&self) -> PathBuf;
fn try_normalize(&self) -> Option<PathBuf>;
fn normalize_sep(&self, seq: &str) -> PathBuf;
fn is_normalized(&self) -> bool;
}
impl NormalizePath for Path {
fn normalize(&self) -> PathBuf {
normalize(self)
}
fn try_normalize(&self) -> Option<PathBuf> {
try_normalize(self)
}
fn normalize_sep(&self, seq: &str) -> PathBuf {
normalize_sep(self, seq)
}
fn is_normalized(&self) -> bool {
is_normalized(self)
}
}