1use std::path::{Path, PathBuf};
4
5use crate::filter::GitignoreFilter;
6
7#[derive(Debug)]
9pub struct GitTrackingRules {
10 repo_path: PathBuf,
11 filter: GitignoreFilter
12}
13
14impl GitTrackingRules {
15 #[must_use]
17 pub fn new(repo_path: &Path) -> Self {
18 Self {
19 repo_path: repo_path.to_path_buf(),
20 filter: GitignoreFilter::new(repo_path)
21 }
22 }
23
24 #[must_use]
26 pub fn accepts(&self, path: &Path) -> bool {
27 if Self::contains_git_directory(path) {
28 return false;
29 }
30
31 let path = if path.is_absolute() {
32 path.to_path_buf()
33 } else {
34 self.repo_path.join(path)
35 };
36
37 !self.filter.is_ignored(&path)
38 }
39
40 pub(crate) fn contains_git_directory(path: &Path) -> bool {
41 path.components().any(|component| component.as_os_str() == ".git")
42 }
43}
44
45#[cfg(test)]
46#[allow(clippy::expect_used)]
47mod tests {
48 use std::path::Path;
49
50 use super::GitTrackingRules;
51
52 #[test]
53 fn tracking_rejects_git_directory() {
54 let tmp = tempfile::tempdir().expect("tempdir");
55 let rules = GitTrackingRules::new(tmp.path());
56
57 assert!(!rules.accepts(Path::new(".git/config")));
58 assert!(!rules.accepts(&tmp.path().join(".git/config")));
59 }
60
61 #[test]
62 fn tracking_uses_gitignore_after_init() {
63 let tmp = tempfile::tempdir().expect("tempdir");
64 std::fs::write(tmp.path().join(".gitignore"), "*.log\nbuild/\n").expect("gitignore");
65 let rules = GitTrackingRules::new(tmp.path());
66
67 assert!(!rules.accepts(&tmp.path().join("debug.log")));
68 assert!(!rules.accepts(&tmp.path().join("build/output.bin")));
69 assert!(rules.accepts(&tmp.path().join("src/lib.rs")));
70 }
71}