use crate::FilterTree;
use git2 as git;
#[derive(clap::Args, Clone)]
pub struct FilterTreeArgs {
pub treeish: String,
#[arg(required = true)]
pub patterns: Vec<String>,
#[arg(short, long, value_enum, default_value = "tree-sha")]
pub format: OutputFormat,
}
#[derive(Clone, Copy, Default, clap::ValueEnum)]
pub enum OutputFormat {
#[default]
TreeSha,
Entries,
Detailed,
}
pub fn filter_tree(
repo: &git::Repository,
treeish: &str,
patterns: &[String],
) -> Result<git::Oid, Box<dyn std::error::Error>> {
let obj = repo.revparse_single(treeish)?;
let tree = obj.peel_to_tree()?;
let pattern_refs: Vec<&str> = patterns.iter().map(|s| s.as_str()).collect();
let filtered = repo.filter_by_patterns(&tree, &pattern_refs)?;
Ok(filtered.id())
}
pub fn print_tree(
repo: &git::Repository,
tree_oid: git::Oid,
format: OutputFormat,
) -> Result<(), Box<dyn std::error::Error>> {
let tree = repo.find_tree(tree_oid)?;
match format {
OutputFormat::TreeSha => {
println!("{}", tree.id());
}
OutputFormat::Entries => {
for entry in tree.iter() {
let name = entry.name().unwrap_or("<invalid-utf8>");
let kind = kind_label(entry.kind());
println!("{}\t{}", kind, name);
}
}
OutputFormat::Detailed => {
println!("Tree: {}", tree.id());
println!("Entries: {}", tree.len());
println!();
for entry in tree.iter() {
let name = entry.name().unwrap_or("<invalid-utf8>");
let kind = kind_label(entry.kind());
let mode = entry.filemode();
let id = entry.id();
println!("{:06o} {} {}\t{}", mode, kind, id, name);
}
}
}
Ok(())
}
pub fn run(args: &FilterTreeArgs) -> Result<(), Box<dyn std::error::Error>> {
let repo = git::Repository::open_from_env()?;
let tree_oid = filter_tree(&repo, &args.treeish, &args.patterns)?;
print_tree(&repo, tree_oid, args.format)?;
Ok(())
}
fn kind_label(kind: Option<git::ObjectType>) -> &'static str {
match kind {
Some(git::ObjectType::Blob) => "blob",
Some(git::ObjectType::Tree) => "tree",
Some(git::ObjectType::Commit) => "commit",
_ => "unknown",
}
}