weavatrix_scan/ignore/repository/
configuration.rs1use super::{
2 Error, HashMap, IgnoreRules, IgnoreSourceKind, OverrideRules, Path, RepositoryMatcher, Result,
3 ScanOptions, SourceRank, find_repository_root, gitconfig_excludes_path,
4 normalized_evidence_location, normalized_relative_path, resolve_git_directory, source_enabled,
5};
6
7impl RepositoryMatcher {
8 pub fn new(root: impl AsRef<Path>) -> Result<Self> {
14 Self::with_options(root, &ScanOptions::default())
15 }
16
17 pub fn with_options(root: impl AsRef<Path>, options: &ScanOptions) -> Result<Self> {
24 let requested = root.as_ref();
25 let scan_root = requested
26 .canonicalize()
27 .map_err(|source| Error::io(requested, source))?;
28 if !scan_root.is_dir() {
29 return Err(Error::InvalidRoot(scan_root));
30 }
31 let repository_root = find_repository_root(&scan_root);
32 let git_sources_enabled = !options.ignore_policy.require_git || repository_root.is_some();
33 let match_root = if options.ignore_policy.parent_rules {
34 repository_root.clone().unwrap_or_else(|| scan_root.clone())
35 } else {
36 scan_root.clone()
37 };
38 let (overrides, override_errors, override_evidence) =
39 OverrideRules::new(&options.override_rules, options.ignore_case_insensitive);
40 let mut matcher = Self {
41 scan_base: scan_root
42 .strip_prefix(&match_root)
43 .map_or_else(|_| String::new(), normalized_relative_path),
44 scan_root,
45 options: options.clone(),
46 match_root,
47 ignore_files: options
48 .ignore_files
49 .iter()
50 .filter(|name| source_enabled(name, &options.ignore_policy, git_sources_enabled))
51 .cloned()
52 .collect(),
53 case_insensitive: options.ignore_case_insensitive,
54 error_policy: options.walk.error_policy,
55 overrides,
56 skip_hidden: options.skip_hidden,
57 base_rules: IgnoreRules::default(),
58 directories: HashMap::new(),
59 sources: override_evidence.into_iter().collect(),
60 warnings: Vec::new(),
61 portable: true,
62 };
63 matcher.handle_errors(override_errors)?;
64 matcher.load_configured_sources(
65 options,
66 repository_root.as_deref(),
67 git_sources_enabled,
68 )?;
69 let root = matcher.match_root.clone();
70 matcher.prepare_directory_inner(&root)?;
71 if matcher.scan_root != matcher.match_root {
72 let scan_root = matcher.scan_root.clone();
73 matcher.prepare_directory_inner(&scan_root)?;
74 }
75 Ok(matcher)
76 }
77
78 fn load_configured_sources(
79 &mut self,
80 options: &ScanOptions,
81 repository_root: Option<&Path>,
82 git_sources_enabled: bool,
83 ) -> Result<()> {
84 if git_sources_enabled
85 && options.ignore_policy.git_global
86 && let Some(path) = gitconfig_excludes_path(repository_root)
87 {
88 self.load_static_source(
89 &path,
90 "",
91 SourceRank::GitGlobal,
92 IgnoreSourceKind::GitGlobal,
93 "<git-global>",
94 )?;
95 self.portable &= !path.is_file();
96 }
97 if git_sources_enabled
98 && options.ignore_policy.git_exclude
99 && let Some(git_directory) = repository_root.and_then(resolve_git_directory)
100 {
101 let path = git_directory.join("info").join("exclude");
102 self.load_static_source(
103 &path,
104 "",
105 SourceRank::GitExclude,
106 IgnoreSourceKind::GitExclude,
107 ".git/info/exclude",
108 )?;
109 self.portable &= !path.is_file();
110 }
111 for path in &options.ignore_policy.explicit_files {
112 self.load_explicit_source(path)?;
113 }
114 Ok(())
115 }
116
117 fn load_explicit_source(&mut self, path: &Path) -> Result<()> {
118 let configured = if path.is_absolute() {
119 path.to_path_buf()
120 } else {
121 self.scan_root.join(path)
122 };
123 let absolute = configured.canonicalize().unwrap_or(configured);
124 let location = normalized_evidence_location(&absolute, &self.match_root);
125 let base = self
126 .scan_root
127 .strip_prefix(&self.match_root)
128 .map_or_else(|_| String::new(), normalized_relative_path);
129 self.load_static_source(
130 &absolute,
131 &base,
132 SourceRank::Explicit,
133 IgnoreSourceKind::Explicit,
134 &location,
135 )?;
136 if absolute.is_file() && !absolute.starts_with(&self.match_root) {
137 self.portable = false;
138 }
139 Ok(())
140 }
141}