git_prole/
only_paths_in_parent_directory.rs

1use std::fmt::Debug;
2
3use camino::Utf8Path;
4use miette::IntoDiagnostic;
5use rustc_hash::FxHashSet;
6use tracing::instrument;
7
8/// Check if a set of paths all have the same parent directory and they are the only paths in that
9/// directory (other than dotfiles).
10#[instrument(level = "trace")]
11pub fn only_paths_in_parent_directory<'p, I, P>(paths: I) -> Option<&'p Utf8Path>
12where
13    I: IntoIterator<Item = &'p P> + Debug,
14    P: AsRef<Utf8Path> + 'p + ?Sized,
15{
16    let mut paths = paths.into_iter();
17    let mut names = FxHashSet::default();
18    let first = paths.next()?.as_ref();
19    let parent = first.parent()?;
20    names.insert(first.file_name()?);
21
22    for path in paths {
23        let path = path.as_ref();
24        if path.parent()? != parent {
25            return None;
26        }
27        names.insert(path.file_name()?);
28    }
29
30    match path_contains_only_names_and_dotfiles(parent, &names) {
31        Ok(true) => Some(parent),
32        Ok(false) => None,
33        Err(error) => {
34            tracing::debug!(
35                directory=%parent,
36                error=%error,
37                "Error while listing directory"
38            );
39            None
40        }
41    }
42}
43
44/// Check if a path contains only files listed in the given set of names and dotfiles.
45#[instrument(level = "trace")]
46fn path_contains_only_names_and_dotfiles(
47    path: &Utf8Path,
48    names: &FxHashSet<&str>,
49) -> miette::Result<bool> {
50    for entry in path.read_dir_utf8().into_diagnostic()? {
51        let entry = entry.into_diagnostic()?;
52        let name = entry.file_name();
53        if !name.starts_with('.') && !names.contains(name) {
54            tracing::debug!(
55                directory=%path,
56                entry=%name,
57                "Directory entry is not a dotfile or listed in known paths"
58            );
59            return Ok(false);
60        }
61    }
62
63    Ok(true)
64}