Skip to main content

vyn_core/
ignore.rs

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            // Always ignore vyn internals and git internals.
21            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}