1use crate::FilterTree;
2use git2 as git;
3
4#[derive(clap::Args, Clone)]
9pub struct FilterTreeArgs {
10 pub treeish: String,
12
13 #[arg(required = true)]
15 pub patterns: Vec<String>,
16
17 #[arg(short, long, value_enum, default_value = "tree-sha")]
19 pub format: OutputFormat,
20}
21
22#[derive(Clone, Copy, Default, clap::ValueEnum)]
24pub enum OutputFormat {
25 #[default]
27 TreeSha,
28 Entries,
30 Detailed,
32}
33
34pub fn filter_tree(
38 repo: &git::Repository,
39 treeish: &str,
40 patterns: &[String],
41) -> Result<git::Oid, Box<dyn std::error::Error>> {
42 let obj = repo.revparse_single(treeish)?;
43 let tree = obj.peel_to_tree()?;
44
45 let pattern_refs: Vec<&str> = patterns.iter().map(|s| s.as_str()).collect();
46 let filtered = repo.filter_by_patterns(&tree, &pattern_refs)?;
47 Ok(filtered.id())
48}
49
50pub fn print_tree(
52 repo: &git::Repository,
53 tree_oid: git::Oid,
54 format: OutputFormat,
55) -> Result<(), Box<dyn std::error::Error>> {
56 let tree = repo.find_tree(tree_oid)?;
57
58 match format {
59 OutputFormat::TreeSha => {
60 println!("{}", tree.id());
61 }
62 OutputFormat::Entries => {
63 for entry in tree.iter() {
64 let name = entry.name().unwrap_or("<invalid-utf8>");
65 let kind = kind_label(entry.kind());
66 println!("{}\t{}", kind, name);
67 }
68 }
69 OutputFormat::Detailed => {
70 println!("Tree: {}", tree.id());
71 println!("Entries: {}", tree.len());
72 println!();
73 for entry in tree.iter() {
74 let name = entry.name().unwrap_or("<invalid-utf8>");
75 let kind = kind_label(entry.kind());
76 let mode = entry.filemode();
77 let id = entry.id();
78 println!("{:06o} {} {}\t{}", mode, kind, id, name);
79 }
80 }
81 }
82
83 Ok(())
84}
85
86pub fn run(args: &FilterTreeArgs) -> Result<(), Box<dyn std::error::Error>> {
88 let repo = git::Repository::open_from_env()?;
89 let tree_oid = filter_tree(&repo, &args.treeish, &args.patterns)?;
90 print_tree(&repo, tree_oid, args.format)?;
91 Ok(())
92}
93
94fn kind_label(kind: Option<git::ObjectType>) -> &'static str {
95 match kind {
96 Some(git::ObjectType::Blob) => "blob",
97 Some(git::ObjectType::Tree) => "tree",
98 Some(git::ObjectType::Commit) => "commit",
99 _ => "unknown",
100 }
101}