1use std::{
2 collections::VecDeque,
3 path::{Path, PathBuf},
4};
5
6use blake3::{Hash, Hasher};
7
8pub fn hash_file(path: impl AsRef<Path>) -> std::io::Result<Hash> {
9 let mut hasher = Hasher::new();
10 hasher.update_mmap(path)?;
11 Ok(hasher.finalize())
12}
13
14pub fn recurse_list_from_root<P: AsRef<Path>>(
15 root: P,
16 yield_dirs: bool,
17) -> impl Iterator<Item = PathBuf> {
18 let mut stack = VecDeque::new();
19 stack.push_back(root.as_ref().to_owned());
20
21 std::iter::from_fn(move || {
22 while let Some(path) = stack.pop_back() {
23 if path.is_dir() {
24 if let Ok(entries) = path.read_dir() {
25 let filtered = entries.filter_map(Result::ok).map(|a| a.path());
26
27 stack.extend(filtered);
28 }
29
30 if yield_dirs {
31 return Some(path);
32 }
33 } else if path.is_file() {
34 return Some(path);
35 }
36 }
37
38 None
39 })
40}
41
42pub fn recurse_list_dirs<P: AsRef<Path>>(root: P) -> impl Iterator<Item = PathBuf> {
43 let mut stack = VecDeque::new();
44 stack.push_back(root.as_ref().to_owned());
45
46 std::iter::from_fn(move || {
47 while let Some(path) = stack.pop_back() {
48 if path.is_dir() {
49 if let Ok(entries) = path.read_dir() {
50 let filtered = entries.filter_map(Result::ok).map(|a| a.path());
51 stack.extend(filtered);
52 }
53
54 return Some(path);
55 }
56 }
57
58 None
59 })
60}