use std::path::{Component, Path, PathBuf};
pub(crate) fn clean(path: &Path) -> PathBuf {
let mut out = Vec::new();
for comp in path.components() {
match comp {
Component::CurDir => (),
Component::ParentDir => match out.last() {
Some(Component::RootDir) => (),
Some(Component::Normal(_)) => {
out.pop();
}
None
| Some(Component::CurDir)
| Some(Component::ParentDir)
| Some(Component::Prefix(_)) => out.push(comp),
},
comp => out.push(comp),
}
}
if !out.is_empty() {
out.iter().collect()
} else {
PathBuf::from(".")
}
}
pub(crate) fn diff_paths(path: &Path, base: &Path) -> Option<PathBuf> {
if path.is_absolute() != base.is_absolute() {
if path.is_absolute() {
Some(PathBuf::from(path))
} else {
None
}
} else {
let mut ita = path.components();
let mut itb = base.components();
let mut comps = vec![];
loop {
match (ita.next(), itb.next()) {
(None, None) => break,
(Some(a), None) => {
comps.push(a);
comps.extend(ita.by_ref());
break;
}
(None, _) => comps.push(Component::ParentDir),
(Some(a), Some(b)) if comps.is_empty() && a == b => (),
(Some(a), Some(Component::CurDir)) => comps.push(a),
(Some(_), Some(Component::ParentDir)) => return None,
(Some(a), Some(_)) => {
comps.push(Component::ParentDir);
for _ in itb {
comps.push(Component::ParentDir);
}
comps.push(a);
comps.extend(ita.by_ref());
break;
}
}
}
Some(comps.iter().map(|c| c.as_os_str()).collect())
}
}
#[test]
fn test_diff_paths() {
assert_eq!(
diff_paths(Path::new("/foo/bar"), Path::new("/foo/bar/baz")),
Some("../".into())
);
assert_eq!(
diff_paths(Path::new("/foo/bar/baz"), Path::new("/foo/bar")),
Some("baz".into())
);
assert_eq!(
diff_paths(Path::new("/foo/bar/quux"), Path::new("/foo/bar/baz")),
Some("../quux".into())
);
assert_eq!(
diff_paths(Path::new("/foo/bar/baz"), Path::new("/foo/bar/quux")),
Some("../baz".into())
);
assert_eq!(
diff_paths(Path::new("/foo/bar"), Path::new("/foo/bar/quux")),
Some("../".into())
);
assert_eq!(
diff_paths(Path::new("/foo/bar"), Path::new("baz")),
Some("/foo/bar".into())
);
assert_eq!(
diff_paths(Path::new("/foo/bar"), Path::new("/baz")),
Some("../foo/bar".into())
);
assert_eq!(
diff_paths(Path::new("foo"), Path::new("bar")),
Some("../foo".into())
);
}