use std::path::{Path, PathBuf};
pub fn common_path_all<'a>(paths: impl IntoIterator<Item = &'a Path>) -> Option<PathBuf> {
let mut path_iter = paths.into_iter();
let mut result = path_iter.next()?.to_path_buf();
for path in path_iter {
if let Some(r) = common_path(&result, &path) {
result = r;
} else {
return None;
}
}
Some(result.to_path_buf())
}
pub fn common_path<P, Q>(one: P, two: Q) -> Option<PathBuf>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let one = one.as_ref();
let two = two.as_ref();
let one = one.components();
let two = two.components();
let mut final_path = PathBuf::new();
let mut found = false;
let paths = one.zip(two);
for (l, r) in paths {
if l == r {
final_path.push(l.as_os_str());
found = true;
} else {
break;
}
}
if found {
Some(final_path)
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compare_all_paths() {
let one = Path::new("/foo/bar/baz/one.txt");
let two = Path::new("/foo/bar/quux/quuux/two.txt");
let three = Path::new("/foo/bar/baz/foo/bar");
let result = Path::new("/foo/bar");
let path_permutations = vec![
vec![one, two, three],
vec![one, three, two],
vec![two, one, three],
vec![two, three, one],
vec![three, one, two],
vec![three, two, one],
];
for all in path_permutations {
assert_eq!(common_path_all(all).unwrap(), result.to_path_buf())
}
}
#[test]
fn compare_paths() {
let one = Path::new("/foo/bar/baz/one.txt");
let two = Path::new("/foo/bar/quux/quuux/two.txt");
let result = Path::new("/foo/bar");
assert_eq!(common_path(&one, &two).unwrap(), result.to_path_buf())
}
#[test]
fn no_common_path() {
let one = Path::new("/foo/bar");
let two = Path::new("./baz/quux");
assert!(common_path(&one, &two).is_none());
}
}