gitcortex_indexer/
differ.rs1use std::path::{Path, PathBuf};
2
3use git2::{Delta, DiffOptions, Repository};
4use gitcortex_core::error::{GitCortexError, Result};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum FileChange {
11 Added(PathBuf),
12 Modified(PathBuf),
13 Deleted(PathBuf),
14}
15
16impl FileChange {
17 pub fn path(&self) -> &Path {
18 match self {
19 FileChange::Added(p) | FileChange::Modified(p) | FileChange::Deleted(p) => p,
20 }
21 }
22}
23
24pub struct Differ {
28 repo: Repository,
29}
30
31impl Differ {
32 pub fn open(repo_path: &Path) -> Result<Self> {
34 let repo = Repository::discover(repo_path)
35 .map_err(|e| GitCortexError::Git(e.message().to_owned()))?;
36 Ok(Self { repo })
37 }
38
39 pub fn head_sha(&self) -> Result<String> {
41 let head = self
42 .repo
43 .head()
44 .map_err(|e| GitCortexError::Git(e.message().to_owned()))?;
45 let commit = head
46 .peel_to_commit()
47 .map_err(|e| GitCortexError::Git(e.message().to_owned()))?;
48 Ok(commit.id().to_string())
49 }
50
51 pub fn changed_files(
58 &self,
59 from_sha: Option<&str>,
60 supported_exts: &[&str],
61 ) -> Result<Vec<FileChange>> {
62 let head_tree = self
63 .repo
64 .head()
65 .and_then(|h| h.peel_to_commit())
66 .and_then(|c| c.tree())
67 .map_err(|e| GitCortexError::Git(e.message().to_owned()))?;
68
69 let from_tree = match from_sha {
70 None => None,
71 Some(sha) => {
72 let oid = git2::Oid::from_str(sha)
73 .map_err(|e| GitCortexError::Git(e.message().to_owned()))?;
74 let commit = self
75 .repo
76 .find_commit(oid)
77 .map_err(|e| GitCortexError::Git(e.message().to_owned()))?;
78 let tree = commit
79 .tree()
80 .map_err(|e| GitCortexError::Git(e.message().to_owned()))?;
81 Some(tree)
82 }
83 };
84
85 let mut opts = DiffOptions::new();
86 opts.ignore_whitespace(false);
87
88 let diff = self
89 .repo
90 .diff_tree_to_tree(from_tree.as_ref(), Some(&head_tree), Some(&mut opts))
91 .map_err(|e| GitCortexError::Git(e.message().to_owned()))?;
92
93 let mut changes: Vec<FileChange> = Vec::new();
94
95 diff.foreach(
96 &mut |delta, _progress| {
97 let change = match delta.status() {
98 Delta::Added | Delta::Copied | Delta::Renamed => delta
99 .new_file()
100 .path()
101 .map(|p| FileChange::Added(p.to_owned())),
102 Delta::Modified => delta
103 .new_file()
104 .path()
105 .map(|p| FileChange::Modified(p.to_owned())),
106 Delta::Deleted => delta
107 .old_file()
108 .path()
109 .map(|p| FileChange::Deleted(p.to_owned())),
110 _ => None,
111 };
112
113 if let Some(c) = change {
114 let ext = c.path().extension().and_then(|e| e.to_str());
115 if ext.map(|e| supported_exts.contains(&e)).unwrap_or(false) {
116 changes.push(c);
117 }
118 }
119 true
120 },
121 None,
122 None,
123 None,
124 )
125 .map_err(|e| GitCortexError::Git(e.message().to_owned()))?;
126
127 Ok(changes)
128 }
129}