gitdataai/libs/git/tree/query.rs

131 lines
4.4 KiB
Rust

//! Tree query operations.
use std::path::Path;
use crate::commit::types::CommitOid;
use crate::tree::types::{TreeEntry, TreeInfo};
use crate::{GitDomain, GitError, GitResult};
impl GitDomain {
fn resolve_tree(&self, oid: &CommitOid) -> GitResult<git2::Tree<'_>> {
let oid = oid
.to_oid()
.map_err(|_| GitError::InvalidOid(oid.to_string()))?;
let obj = self
.repo()
.find_object(oid, None)
.map_err(|_| GitError::ObjectNotFound(oid.to_string()))?;
match obj.kind() {
Some(git2::ObjectType::Commit) => {
let commit = obj.as_commit().ok_or_else(|| {
GitError::Internal("object type mismatch: expected commit".into())
})?;
self.repo()
.find_tree(commit.tree_id())
.map_err(|e| GitError::Internal(e.to_string()))
}
Some(git2::ObjectType::Tree) => obj
.peel_to_tree()
.map_err(|e| GitError::Internal(e.to_string())),
_ => Err(GitError::InvalidOid(oid.to_string())),
}
}
pub fn tree_get(&self, oid: &CommitOid) -> GitResult<TreeInfo> {
let tree = self.resolve_tree(oid)?;
Ok(TreeInfo::from_git2(&tree))
}
pub fn tree_exists(&self, oid: &CommitOid) -> bool {
self.resolve_tree(oid).is_ok()
}
pub fn tree_entry(&self, oid: &CommitOid, index: usize) -> GitResult<TreeEntry> {
let tree = self.resolve_tree(oid)?;
let entry = tree
.get(index)
.ok_or_else(|| GitError::Internal("tree entry not found".to_string()))?;
Ok(TreeEntry::from_git2(entry, self.repo()))
}
pub fn tree_list(&self, oid: &CommitOid) -> GitResult<Vec<TreeEntry>> {
let tree = self.resolve_tree(oid)?;
let repo = self.repo();
let entries: Vec<TreeEntry> = tree
.iter()
.map(|entry| TreeEntry::from_git2(entry, repo))
.collect();
Ok(entries)
}
pub fn tree_entry_count(&self, oid: &CommitOid) -> GitResult<usize> {
let info = self.tree_get(oid)?;
Ok(info.entry_count)
}
pub fn tree_entry_by_path(&self, tree_oid: &CommitOid, path: &str) -> GitResult<TreeEntry> {
let tree = self.resolve_tree(tree_oid)?;
let entry = tree
.get_path(Path::new(path))
.map_err(|e| GitError::Internal(format!("path '{}': {}", path, e)))?;
Ok(TreeEntry::from_git2(entry, self.repo()))
}
pub fn tree_entry_by_path_from_commit(
&self,
commit_oid: &CommitOid,
path: &str,
) -> GitResult<TreeEntry> {
let oid = commit_oid
.to_oid()
.map_err(|_| GitError::InvalidOid(commit_oid.to_string()))?;
let commit = self
.repo()
.find_commit(oid)
.map_err(|_| GitError::ObjectNotFound(commit_oid.to_string()))?;
let tree = self
.repo()
.find_tree(commit.tree_id())
.map_err(|e| GitError::Internal(e.to_string()))?;
let entry = tree
.get_path(Path::new(path))
.map_err(|e| GitError::Internal(format!("path '{}': {}", path, e)))?;
Ok(TreeEntry::from_git2(entry, self.repo()))
}
pub fn tree_is_empty(&self, oid: &CommitOid) -> GitResult<bool> {
let info = self.tree_get(oid)?;
Ok(info.is_empty)
}
pub fn tree_diffstats(
&self,
old_tree: &CommitOid,
new_tree: &CommitOid,
) -> GitResult<crate::diff::types::DiffStats> {
use crate::diff::types::DiffStats;
let old_oid = old_tree
.to_oid()
.map_err(|_| GitError::InvalidOid(old_tree.to_string()))?;
let new_oid = new_tree
.to_oid()
.map_err(|_| GitError::InvalidOid(new_tree.to_string()))?;
let old_tree = self
.repo()
.find_tree(old_oid)
.map_err(|e| GitError::Internal(e.to_string()))?;
let new_tree = self
.repo()
.find_tree(new_oid)
.map_err(|e| GitError::Internal(e.to_string()))?;
let diff = self
.repo()
.diff_tree_to_tree(Some(&old_tree), Some(&new_tree), None)
.map_err(|e| GitError::Internal(e.to_string()))?;
let stats = diff
.stats()
.map_err(|e| GitError::Internal(e.to_string()))?;
Ok(DiffStats::from_git2(&stats))
}
}