1use ignore::gitignore::{Gitignore, GitignoreBuilder};
2use std::path::{Path, PathBuf};
3use thiserror::Error;
4
5#[derive(Debug)]
6pub struct IgnoreMatcher {
7 root: PathBuf,
8 gitignore: Gitignore,
9}
10
11#[derive(Debug, Error)]
12pub enum IgnoreError {
13 #[error("failed to build ignore matcher")]
14 BuildFailure,
15}
16
17impl IgnoreMatcher {
18 pub fn should_ignore(&self, path: &Path, is_dir: bool) -> bool {
19 if let Ok(rel_path) = path.strip_prefix(&self.root) {
20 if rel_path == Path::new(".vyn") || rel_path.starts_with(Path::new(".vyn")) {
22 return true;
23 }
24 if rel_path == Path::new(".git") || rel_path.starts_with(Path::new(".git")) {
25 return true;
26 }
27
28 return self.gitignore.matched(rel_path, is_dir).is_ignore();
29 }
30
31 true
32 }
33}
34
35pub fn load_ignore_matcher(root: &Path) -> Result<IgnoreMatcher, IgnoreError> {
36 let mut builder = GitignoreBuilder::new(root);
37 let ignore_file = root.join(".vynignore");
38 if ignore_file.exists() {
39 builder.add(ignore_file);
40 }
41
42 let gitignore = builder.build().map_err(|_| IgnoreError::BuildFailure)?;
43
44 Ok(IgnoreMatcher {
45 root: root.to_path_buf(),
46 gitignore,
47 })
48}
49
50#[cfg(test)]
51mod tests {
52 use super::load_ignore_matcher;
53 use std::fs;
54 use std::path::Path;
55 use uuid::Uuid;
56
57 #[test]
58 fn ignore_patterns() {
59 let tmp = std::env::temp_dir().join(format!("vyn-ignore-{}", Uuid::new_v4()));
60 fs::create_dir_all(&tmp).expect("temp dir should be created");
61 fs::write(tmp.join(".vynignore"), "*.tmp\nsecrets/**\n")
62 .expect("ignore file should be written");
63 fs::create_dir_all(tmp.join("secrets")).expect("secrets directory should be created");
64 fs::write(tmp.join("secrets").join("token.env"), "TOKEN=abc\n")
65 .expect("secrets token file should be written");
66
67 let matcher = load_ignore_matcher(&tmp).expect("matcher should load");
68 assert!(matcher.should_ignore(&tmp.join("foo.tmp"), false));
69 assert!(matcher.should_ignore(&tmp.join("secrets").join("token.env"), false));
70 assert!(!matcher.should_ignore(&tmp.join("app.env"), false));
71 assert!(matcher.should_ignore(&tmp.join(".vyn").join("manifest.json"), false));
72 assert!(matcher.should_ignore(&tmp.join(".git").join("config"), false));
73
74 fs::remove_dir_all(Path::new(&tmp)).expect("temp dir should be removed");
75 }
76}