1use std::path::Path;
2
3use anyhow::{Context, Result};
4use pgit_core::hash;
5
6pub fn copy_dir(src: &Path, dest: &Path) -> Result<()> {
8 std::fs::create_dir_all(dest)
9 .with_context(|| format!("create {}", dest.display()))?;
10
11 for entry in std::fs::read_dir(src)
12 .with_context(|| format!("read {}", src.display()))?
13 {
14 let entry = entry.with_context(|| format!("read entry in {}", src.display()))?;
15 if entry.file_name() == ".git" {
16 continue;
17 }
18 let src_path = entry.path();
19 let dest_path = dest.join(entry.file_name());
20 if src_path.is_dir() {
21 copy_dir(&src_path, &dest_path)?;
22 } else {
23 std::fs::copy(&src_path, &dest_path).with_context(|| {
24 format!("copy {} -> {}", src_path.display(), dest_path.display())
25 })?;
26 }
27 }
28 Ok(())
29}
30
31pub fn collect_tree(root: &Path) -> Result<Vec<(String, Vec<u8>)>> {
34 let mut entries = Vec::new();
35 collect_recursive(root, root, &mut entries)?;
36 Ok(entries)
37}
38
39fn collect_recursive(
40 root: &Path,
41 current: &Path,
42 out: &mut Vec<(String, Vec<u8>)>,
43) -> Result<()> {
44 for entry in std::fs::read_dir(current)
45 .with_context(|| format!("read {}", current.display()))?
46 {
47 let entry = entry?;
48 if entry.file_name() == ".git" {
49 continue;
50 }
51 let path = entry.path();
52 if path.is_dir() {
53 collect_recursive(root, &path, out)?;
54 } else {
55 let rel = path
56 .strip_prefix(root)
57 .unwrap()
58 .to_string_lossy()
59 .into_owned();
60 let bytes = std::fs::read(&path)
61 .with_context(|| format!("read {}", path.display()))?;
62 out.push((rel, bytes));
63 }
64 }
65 Ok(())
66}
67
68pub fn hash_dir(root: &Path) -> Result<String> {
70 let mut entries = collect_tree(root)?;
71 Ok(hash::sha256_tree(&mut entries))
72}