use crate::prelude::*;
pub struct Shortener;
impl Shortener {
#[must_use]
pub fn longest_common_prefix(paths: &[impl AsRef<Path>]) -> Option<PathBuf> {
let first = paths.first()?;
let mut prefix = first.as_ref().to_path_buf();
for path in paths.iter().skip(1) {
while !path.as_ref().starts_with(&prefix) {
if !prefix.pop() {
return None;
}
}
}
if prefix.as_os_str().is_empty() {
None
} else {
Some(prefix)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(clippy::too_many_lines)]
fn longest_common_prefix_tests() {
assert_eq!(
Shortener::longest_common_prefix(&[p("a/b/c"), p("a/b/d")]),
Some(p("a/b"))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/b/c"), p("a/b/c"), p("a/b/")]),
Some(p("a/b"))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/b/c"), p("a/b/c")]),
Some(p("a/b/c"))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/b/c"), p("x/y")]),
None
);
assert_eq!(
Shortener::longest_common_prefix(&Vec::<PathBuf>::new()),
None
);
assert_eq!(Shortener::longest_common_prefix(&[p("")]), None);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/b/c"), p(""), p(""), p("")]),
None
);
assert_eq!(
Shortener::longest_common_prefix(&[p("/a"), p("/b")]),
Some(p("/"))
);
assert_eq!(Shortener::longest_common_prefix(&[p("a"), p("b")]), None);
assert_eq!(Shortener::longest_common_prefix(&[p(".")]), Some(p(".")));
assert_eq!(Shortener::longest_common_prefix(&[p("./")]), Some(p("./")));
assert_eq!(
Shortener::longest_common_prefix(&[p("."), p(".")]),
Some(p("."))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("./a"), p("./b")]),
Some(p("."))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("./a/b"), p("./a/c")]),
Some(p("./a"))
);
assert_eq!(Shortener::longest_common_prefix(&[p("./a"), p("a")]), None);
assert_eq!(
Shortener::longest_common_prefix(&[p("../a"), p("../b")]),
Some(p(".."))
);
assert_eq!(
Shortener::longest_common_prefix(&[p(".."), p("..")]),
Some(p(".."))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("~/a"), p("~/b")]),
Some(p("~"))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("~"), p("~")]),
Some(p("~"))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/../b"), p("a/../c")]),
Some(p("a/.."))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/b/../c"), p("a/b/../d")]),
Some(p("a/b/.."))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/./b"), p("a/./c")]),
Some(p("a/."))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/../b"), p("a/b")]),
Some(p("a"))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/../../b"), p("a/../../c")]),
Some(p("a/../.."))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/b/"), p("a/b/")]),
Some(p("a/b/"))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/b/"), p("a/b")]),
Some(p("a/b"))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/../b"), p("c/../b")]),
None
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/b"), p("a/b/c")]),
Some(p("a/b"))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/b/c"), p("a/b")]),
Some(p("a/b"))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/b/c"), p("a/b/d"), p("a/b/e"), p("a/b/f")]),
Some(p("a/b"))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/b/c"), p("a/b/d"), p("a/x/e")]),
Some(p("a"))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a b/c d"), p("a b/e f")]),
Some(p("a b"))
);
assert_eq!(
Shortener::longest_common_prefix(&[
p("音楽/アルバム/曲.flac"),
p("音楽/アルバム/別曲.flac")
]),
Some(p("音楽/アルバム"))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("музика/альбом"), p("музика/інший")]),
Some(p("музика"))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/b/c"), p("a/b/c"), p("a/b/c")]),
Some(p("a/b/c"))
);
assert_eq!(
Shortener::longest_common_prefix(&[p("a/b/c")]),
Some(p("a/b/c"))
);
}
fn p(path: &str) -> PathBuf {
PathBuf::from(path)
}
}