use std::path::{Path, PathBuf};
pub fn nearest_existing<F>(start: &Path, exists: F) -> Option<PathBuf>
where
F: Fn(&Path) -> bool,
{
start
.ancestors()
.find(|candidate| {
!candidate.as_os_str().is_empty() && exists(candidate)
})
.map(Path::to_path_buf)
}
pub fn nearest_existing_on_disk(start: &Path) -> Option<PathBuf> {
nearest_existing(start, |candidate| candidate.exists())
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use super::*;
fn exists_set(paths: &[&str]) -> HashSet<PathBuf> {
paths.iter().map(PathBuf::from).collect()
}
#[test]
fn returns_start_when_it_exists() {
let present = exists_set(&["/a/b/c"]);
let found =
nearest_existing(Path::new("/a/b/c"), |p| present.contains(p));
assert_eq!(found, Some(PathBuf::from("/a/b/c")));
}
#[test]
fn walks_up_to_first_existing_parent() {
let present = exists_set(&["/a", "/a/b"]);
let found =
nearest_existing(Path::new("/a/b/c/d"), |p| present.contains(p));
assert_eq!(found, Some(PathBuf::from("/a/b")));
}
#[test]
fn none_when_no_ancestor_exists() {
let found = nearest_existing(Path::new("/x/y/z"), |_| false);
assert_eq!(found, None);
}
}