lechange_core/patterns/
matcher.rs1use crate::error::Result;
4use crate::interner::StringInterner;
5use crate::types::ChangedFile;
6use globset::{Glob, GlobSet, GlobSetBuilder};
7use rayon::prelude::*;
8
9pub struct PatternMatcher {
11 include_set: GlobSet,
12 exclude_set: GlobSet,
13 negation_first: bool,
14}
15
16impl PatternMatcher {
17 pub fn new(includes: &[&str], excludes: &[&str], negation_first: bool) -> Result<Self> {
19 let mut include_builder = GlobSetBuilder::new();
20 for pattern in includes {
21 include_builder.add(Glob::new(pattern)?);
22 }
23
24 let mut exclude_builder = GlobSetBuilder::new();
25 for pattern in excludes {
26 exclude_builder.add(Glob::new(pattern)?);
27 }
28
29 Ok(Self {
30 include_set: include_builder.build()?,
31 exclude_set: exclude_builder.build()?,
32 negation_first,
33 })
34 }
35
36 #[inline]
38 pub fn matches_sync(&self, path: &str) -> bool {
39 if self.negation_first {
40 if self.exclude_set.is_match(path) {
41 return false;
42 }
43 self.include_set.is_empty() || self.include_set.is_match(path)
44 } else {
45 if !self.include_set.is_empty() && !self.include_set.is_match(path) {
46 return false;
47 }
48 !self.exclude_set.is_match(path)
49 }
50 }
51
52 pub fn partition_files_parallel(
56 &self,
57 files: &[ChangedFile],
58 interner: &StringInterner,
59 ) -> (Vec<u32>, Vec<u32>) {
60 use rayon::iter::Either;
61
62 (0..files.len() as u32).into_par_iter().partition_map(|i| {
63 let file = &files[i as usize];
64 match interner.resolve(file.path) {
65 Some(path) if self.matches_sync(path) => Either::Left(i),
66 _ => Either::Right(i),
67 }
68 })
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn test_basic_matching() {
78 let matcher = PatternMatcher::new(&["**/*.rs"], &[], false).unwrap();
79
80 assert!(matcher.matches_sync("src/main.rs"));
81 assert!(matcher.matches_sync("lib/mod.rs"));
82 assert!(!matcher.matches_sync("README.md"));
83 }
84
85 #[test]
86 fn test_exclusion() {
87 let matcher = PatternMatcher::new(&["**/*.rs"], &["**/test_*.rs"], false).unwrap();
88
89 assert!(matcher.matches_sync("src/main.rs"));
90 assert!(!matcher.matches_sync("src/test_utils.rs"));
91 }
92}