1use {
2 anyhow::Result,
3 glob::Pattern,
4 std::path::{
5 Path,
6 PathBuf,
7 },
8};
9
10mod git_ignorer;
11mod glob_ignorer;
12
13pub use {
14 git_ignorer::GitIgnorer,
15 glob_ignorer::GlobIgnorer,
16};
17
18pub(crate) fn build_glob_patterns(
21 pattern: &str,
22 root: &Path,
23) -> Result<Vec<Pattern>> {
24 let mut patterns = Vec::new();
25 if pattern.starts_with('/') {
26 patterns.push(Pattern::new(pattern)?);
27 let abs_pattern = root.join(pattern);
28 patterns.push(Pattern::new(&abs_pattern.to_string_lossy())?);
29 } else {
30 patterns.push(Pattern::new(&format!("/**/{pattern}"))?);
31 }
32 Ok(patterns)
33}
34
35pub trait Ignorer {
36 fn excludes(
42 &mut self,
43 paths: &Path,
44 ) -> Result<bool>;
45}
46
47#[derive(Default)]
49pub struct IgnorerSet {
50 ignorers: Vec<Box<dyn Ignorer + Send>>,
51 override_globs: Vec<Pattern>,
53}
54impl IgnorerSet {
55 pub fn add(
56 &mut self,
57 ignorer: Box<dyn Ignorer + Send>,
58 ) {
59 self.ignorers.push(ignorer);
60 }
61 pub fn add_override(
65 &mut self,
66 pattern: &str,
67 root: &Path,
68 ) -> Result<()> {
69 self.override_globs
70 .extend(build_glob_patterns(pattern, root)?);
71 Ok(())
72 }
73 fn is_overridden(
75 &self,
76 path: &Path,
77 ) -> bool {
78 for glob in &self.override_globs {
79 if glob.matches_path(path) {
80 return true;
81 }
82 }
83 false
84 }
85 pub fn excludes_all_pathbufs(
86 &mut self,
87 paths: &[PathBuf],
88 ) -> Result<bool> {
89 if self.ignorers.is_empty() {
90 return Ok(false);
91 }
92 for path in paths {
93 if self.is_overridden(path) {
97 return Ok(false);
98 }
99 let mut excluded = false;
100 for ignorer in &mut self.ignorers {
101 if ignorer.excludes(path)? {
102 excluded = true;
103 break;
104 }
105 }
106 if !excluded {
107 return Ok(false);
108 }
109 }
110 Ok(true)
111 }
112}