lets_find_up/
lib.rs

1use std::path::{Path, PathBuf};
2
3pub enum FindUpKind {
4    File,
5    Dir,
6}
7
8pub struct FindUpOptions<'a> {
9    pub cwd: &'a Path,
10    pub kind: FindUpKind,
11}
12
13impl Default for FindUpOptions<'_> {
14    fn default() -> Self {
15        Self {
16            cwd: Path::new("."),
17            kind: FindUpKind::File,
18        }
19    }
20}
21
22#[inline]
23/// Find a file by walking up parent directories from the current directory
24/// at the time the function is invoked.
25pub fn find_up<T: AsRef<Path>>(file_name: T) -> std::io::Result<Option<PathBuf>> {
26    find_up_with(file_name, Default::default())
27}
28
29/// Find a file(default) or directory by walking up parent directories
30pub fn find_up_with<T: AsRef<Path>>(
31    file_name: T,
32    options: FindUpOptions,
33) -> std::io::Result<Option<PathBuf>> {
34    let target_file_name = file_name.as_ref();
35    let cwd_buf = std::env::current_dir().unwrap();
36    let cwd = if options.cwd.eq(Path::new(".")) {
37        Path::new(&cwd_buf)
38    } else {
39        options.cwd
40    };
41    let mut target_dir = Some(cwd);
42    let is_search_dir = matches!(options.kind, FindUpKind::Dir);
43
44    while let Some(dir) = target_dir {
45        let mut dir_iterator = std::fs::read_dir(dir)?.peekable();
46        if dir_iterator.peek().is_none() {
47            target_dir = dir.parent();
48            continue;
49        }
50        for entry in dir_iterator {
51            let entry = entry?;
52            let path = entry.path();
53            if path.is_dir() {
54                if is_search_dir {
55                    if let Some(file_name) = path.file_name() {
56                        if target_file_name == file_name {
57                            return Ok(Some(path));
58                        }
59                    }
60                }
61            } else if let Some(file_name) = path.file_name() {
62                if target_file_name == file_name {
63                    return Ok(Some(path));
64                }
65            }
66            target_dir = dir.parent()
67        }
68    }
69    Ok(None)
70}