git_prole/
only_paths_in_parent_directory.rs1use std::fmt::Debug;
2
3use camino::Utf8Path;
4use miette::IntoDiagnostic;
5use rustc_hash::FxHashSet;
6use tracing::instrument;
7
8#[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#[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}