1use std::path::Path;
2
3use globset::{Glob, GlobBuilder, GlobSet, GlobSetBuilder};
4
5use crate::config::PathsSpec;
6use crate::error::{Error, Result};
7
8#[derive(Debug, Clone)]
15pub struct Scope {
16 include: GlobSet,
17 exclude: GlobSet,
18 has_include: bool,
19}
20
21fn compile(pattern: &str) -> Result<Glob> {
22 GlobBuilder::new(pattern)
23 .literal_separator(true)
24 .build()
25 .map_err(|source| Error::Glob {
26 pattern: pattern.to_string(),
27 source,
28 })
29}
30
31impl Scope {
32 pub fn from_patterns(patterns: &[String]) -> Result<Self> {
33 let mut include = GlobSetBuilder::new();
34 let mut exclude = GlobSetBuilder::new();
35 let mut has_include = false;
36 for pattern in patterns {
37 if let Some(rest) = pattern.strip_prefix('!') {
38 exclude.add(compile(rest)?);
39 } else {
40 include.add(compile(pattern)?);
41 has_include = true;
42 }
43 }
44 Ok(Self {
45 include: include.build().map_err(|source| Error::Glob {
46 pattern: patterns.join(","),
47 source,
48 })?,
49 exclude: exclude.build().map_err(|source| Error::Glob {
50 pattern: patterns.join(","),
51 source,
52 })?,
53 has_include,
54 })
55 }
56
57 pub fn from_paths_spec(spec: &PathsSpec) -> Result<Self> {
58 match spec {
59 PathsSpec::Single(s) => Self::from_patterns(std::slice::from_ref(s)),
60 PathsSpec::Many(v) => Self::from_patterns(v),
61 PathsSpec::IncludeExclude { include, exclude } => {
62 let mut combined = include.clone();
63 for e in exclude {
64 combined.push(format!("!{e}"));
65 }
66 Self::from_patterns(&combined)
67 }
68 }
69 }
70
71 pub fn match_all() -> Self {
73 let mut include = GlobSetBuilder::new();
74 include.add(compile("**").expect("`**` must compile"));
75 Self {
76 include: include.build().expect("`**` GlobSet must build"),
77 exclude: GlobSet::empty(),
78 has_include: true,
79 }
80 }
81
82 pub fn matches(&self, path: &Path) -> bool {
83 if self.exclude.is_match(path) {
84 return false;
85 }
86 if !self.has_include {
87 return true;
88 }
89 self.include.is_match(path)
90 }
91}