weavatrix_git/
snapshot.rs1use std::collections::BTreeMap;
2
3use crate::{EntryKind, GitError, ObjectId, Repository, Result};
4
5#[derive(Clone, Debug, Eq, PartialEq)]
6pub struct SnapshotEntry {
7 pub path: Vec<u8>,
8 pub mode: u32,
9 pub id: ObjectId,
10 pub kind: EntryKind,
11}
12
13#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct CommitSnapshot {
15 pub commit: ObjectId,
16 pub tree: ObjectId,
17 pub entries: Vec<SnapshotEntry>,
18}
19
20impl Repository {
21 pub fn snapshot(&self, revision: &str) -> Result<CommitSnapshot> {
22 at(self, revision)
23 }
24
25 pub fn tree_manifest(&self, tree: ObjectId) -> Result<Vec<SnapshotEntry>> {
26 manifest(self, tree)
27 }
28}
29
30pub(crate) fn at(repository: &Repository, revision: &str) -> Result<CommitSnapshot> {
31 let commit = repository.resolve(revision)?;
32 let tree = repository.commit_metadata(commit)?.tree;
33 Ok(CommitSnapshot {
34 commit,
35 tree,
36 entries: manifest(repository, tree)?,
37 })
38}
39
40pub(crate) fn manifest(repository: &Repository, tree: ObjectId) -> Result<Vec<SnapshotEntry>> {
41 let mut entries = BTreeMap::new();
42 flatten(repository, tree, &[], 0, &mut entries)?;
43 Ok(entries.into_values().collect())
44}
45
46fn flatten(
47 repository: &Repository,
48 tree: ObjectId,
49 prefix: &[u8],
50 depth: usize,
51 output: &mut BTreeMap<Vec<u8>, SnapshotEntry>,
52) -> Result<()> {
53 if depth >= repository.limits().max_tree_depth {
54 return Err(GitError::LimitExceeded {
55 resource: "tree depth",
56 limit: repository.limits().max_tree_depth,
57 });
58 }
59 for entry in repository.tree(tree)?.entries {
60 let path = join(prefix, &entry.name);
61 if entry.kind == EntryKind::Tree {
62 flatten(repository, entry.id, &path, depth + 1, output)?;
63 continue;
64 }
65 if output.len() >= repository.limits().max_tree_entries {
66 return Err(GitError::LimitExceeded {
67 resource: "snapshot entries",
68 limit: repository.limits().max_tree_entries,
69 });
70 }
71 output.insert(
72 path.clone(),
73 SnapshotEntry {
74 path,
75 mode: entry.mode,
76 id: entry.id,
77 kind: entry.kind,
78 },
79 );
80 }
81 Ok(())
82}
83
84fn join(prefix: &[u8], name: &[u8]) -> Vec<u8> {
85 let mut path = Vec::with_capacity(prefix.len() + name.len() + usize::from(!prefix.is_empty()));
86 if !prefix.is_empty() {
87 path.extend_from_slice(prefix);
88 path.push(b'/');
89 }
90 path.extend_from_slice(name);
91 path
92}