use std::path::{Component, Path, PathBuf};
#[derive(Debug)]
pub struct NormalizeError;
impl std::fmt::Display for NormalizeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Path normalization failed")
}
}
impl std::error::Error for NormalizeError {}
pub trait PathNormalizeExt {
fn normalize_path(&self) -> Result<PathBuf, NormalizeError>;
}
impl PathNormalizeExt for Path {
fn normalize_path(&self) -> Result<PathBuf, NormalizeError> {
let mut lexical = PathBuf::new();
let mut iter = self.components().peekable();
let root = match iter.peek() {
Some(Component::ParentDir) => return Err(NormalizeError),
Some(p @ Component::RootDir) | Some(p @ Component::CurDir) => {
lexical.push(p);
iter.next();
lexical.as_os_str().len()
}
Some(Component::Prefix(prefix)) => {
lexical.push(prefix.as_os_str());
iter.next();
if let Some(p @ Component::RootDir) = iter.peek() {
lexical.push(p);
iter.next();
}
lexical.as_os_str().len()
}
None => return Ok(PathBuf::new()),
Some(Component::Normal(_)) => 0,
};
for component in iter {
match component {
Component::RootDir => unreachable!(),
Component::Prefix(_) => return Err(NormalizeError),
Component::CurDir => continue,
Component::ParentDir => {
if lexical.as_os_str().len() == root {
return Err(NormalizeError);
} else {
lexical.pop();
}
}
Component::Normal(path) => lexical.push(path),
}
}
Ok(lexical)
}
}