Skip to main content

weavatrix_git/
snapshot.rs

1use 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}