diff options
Diffstat (limited to 'bin/src/dirs.rs')
-rw-r--r-- | bin/src/dirs.rs | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/bin/src/dirs.rs b/bin/src/dirs.rs new file mode 100644 index 0000000..d3fe612 --- /dev/null +++ b/bin/src/dirs.rs | |||
@@ -0,0 +1,99 @@ | |||
1 | use std::{ | ||
2 | fs, | ||
3 | io::{self, Error, ErrorKind}, | ||
4 | path::{Path, PathBuf}, | ||
5 | }; | ||
6 | |||
7 | use crate::dirs; | ||
8 | |||
9 | use ignore::{ | ||
10 | gitignore::{Gitignore, GitignoreBuilder}, | ||
11 | Error as IgnoreError, Match, | ||
12 | }; | ||
13 | |||
14 | #[derive(Debug)] | ||
15 | pub struct Walker { | ||
16 | dirs: Vec<PathBuf>, | ||
17 | files: Vec<PathBuf>, | ||
18 | ignore: Gitignore, | ||
19 | } | ||
20 | |||
21 | impl Walker { | ||
22 | pub fn new<P: AsRef<Path>>(target: P, ignore: Gitignore) -> io::Result<Self> { | ||
23 | let target = target.as_ref().to_path_buf(); | ||
24 | if !target.exists() { | ||
25 | Err(Error::new( | ||
26 | ErrorKind::NotFound, | ||
27 | format!("file not found: {}", target.display()), | ||
28 | )) | ||
29 | } else if target.is_dir() { | ||
30 | Ok(Self { | ||
31 | dirs: vec![target], | ||
32 | files: vec![], | ||
33 | ignore, | ||
34 | }) | ||
35 | } else { | ||
36 | Ok(Self { | ||
37 | dirs: vec![], | ||
38 | files: vec![target], | ||
39 | ignore, | ||
40 | }) | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | |||
45 | impl Iterator for Walker { | ||
46 | type Item = PathBuf; | ||
47 | fn next(&mut self) -> Option<Self::Item> { | ||
48 | if let Some(dir) = self.dirs.pop() { | ||
49 | if dir.is_dir() { | ||
50 | if let Match::None | Match::Whitelist(_) = self.ignore.matched(&dir, true) { | ||
51 | for entry in fs::read_dir(&dir).ok()? { | ||
52 | let entry = entry.ok()?; | ||
53 | let path = entry.path(); | ||
54 | if path.is_dir() { | ||
55 | self.dirs.push(path); | ||
56 | } else if path.is_file() { | ||
57 | self.files.push(path); | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | self.files.pop() | ||
64 | } | ||
65 | } | ||
66 | |||
67 | pub fn build_ignore_set<P: AsRef<Path>>( | ||
68 | ignore: &[String], | ||
69 | target: P, | ||
70 | unrestricted: bool, | ||
71 | ) -> Result<Gitignore, IgnoreError> { | ||
72 | let gitignore_path = target.as_ref().join(".gitignore"); | ||
73 | |||
74 | // Looks like GitignoreBuilder::new does not source globs | ||
75 | // within gitignore_path by default, we have to enforce that | ||
76 | // using GitignoreBuilder::add. Probably a bug in the ignore | ||
77 | // crate? | ||
78 | let mut gitignore = GitignoreBuilder::new(&gitignore_path); | ||
79 | |||
80 | // if we are to "restrict" aka "respect" .gitignore, then | ||
81 | // add globs from gitignore path as well | ||
82 | if !unrestricted { | ||
83 | gitignore.add(&gitignore_path); | ||
84 | } | ||
85 | |||
86 | for i in ignore { | ||
87 | gitignore.add_line(None, i.as_str())?; | ||
88 | } | ||
89 | |||
90 | gitignore.build() | ||
91 | } | ||
92 | |||
93 | pub fn walk_nix_files<P: AsRef<Path>>( | ||
94 | ignore: Gitignore, | ||
95 | target: P, | ||
96 | ) -> Result<impl Iterator<Item = PathBuf>, io::Error> { | ||
97 | let walker = dirs::Walker::new(target, ignore)?; | ||
98 | Ok(walker.filter(|path: &PathBuf| matches!(path.extension(), Some(e) if e == "nix"))) | ||
99 | } | ||