1#![doc = include_str!("../README.md")]
2
3use {
4 anyhow::{Result, anyhow},
5 ignore::gitignore::{Gitignore, GitignoreBuilder, gitconfig_excludes_path},
6 std::{
7 collections::BTreeSet,
8 path::{Path, PathBuf},
9 },
10};
11
12pub fn ignored(path: impl AsRef<Path>) -> Result<bool> {
14 Ok(Ignore::new(path.as_ref().parent().unwrap())?.check(path))
15}
16
17pub struct Ignore {
18 matcher: Gitignore,
19}
20
21impl Default for Ignore {
22 fn default() -> Ignore {
23 Ignore::new("").unwrap()
24 }
25}
26
27impl Ignore {
28 pub fn new(root: impl AsRef<Path>) -> Result<Ignore> {
29 let mut builder = GitignoreBuilder::new(&root);
30
31 let mut added = BTreeSet::new();
33 let mut dir = root.as_ref().to_path_buf();
34 loop {
35 add_path(dir.join(".gitignore"), &mut builder, &mut added)?;
36
37 if let Some(parent) = dir.parent() {
38 dir = parent.to_path_buf();
39 } else {
40 break;
41 }
42 }
43
44 if let Some(path) = gitconfig_excludes_path() {
46 add_path(path, &mut builder, &mut added)?;
47 }
48
49 Ok(Ignore {
50 matcher: builder.build()?,
51 })
52 }
53
54 pub fn check(&self, path: impl AsRef<Path>) -> bool {
55 self.matcher
56 .matched_path_or_any_parents(&path, path.as_ref().is_dir())
57 .is_ignore()
58 }
59}
60
61fn add_path(
62 path: PathBuf,
63 builder: &mut GitignoreBuilder,
64 added: &mut BTreeSet<PathBuf>,
65) -> Result<()> {
66 if path.exists() && !added.contains(&path) {
67 match builder.add(&path) {
68 Some(e) => Err(anyhow!("Failed to add {path:?}: {e}")),
69 None => {
70 added.insert(path);
71 Ok(())
72 }
73 }
74 } else {
75 Ok(())
76 }
77}