Lints and suggestions for the Nix programming language
at main 112 lines 3.3 kB view raw
1use std::{ 2 fs, 3 io::{self, Error, ErrorKind}, 4 path::{Path, PathBuf}, 5}; 6 7use crate::dirs; 8 9use ignore::{ 10 Error as IgnoreError, Match, 11 gitignore::{Gitignore, GitignoreBuilder}, 12}; 13 14#[derive(Debug)] 15pub struct Walker { 16 dirs: Vec<PathBuf>, 17 files: Vec<PathBuf>, 18 ignore: Gitignore, 19} 20 21impl 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 45impl Iterator for Walker { 46 type Item = PathBuf; 47 fn next(&mut self) -> Option<Self::Item> { 48 self.files.pop().or_else(|| { 49 while let Some(dir) = self.dirs.pop() { 50 if dir.is_dir() 51 && let Match::None | Match::Whitelist(_) = self.ignore.matched(&dir, true) 52 { 53 let mut found = false; 54 for entry in fs::read_dir(&dir).ok()? { 55 let entry = entry.ok()?; 56 let path = entry.path(); 57 if path.is_dir() { 58 self.dirs.push(path); 59 } else if path.is_file() 60 && let Match::None | Match::Whitelist(_) = 61 self.ignore.matched(&path, false) 62 { 63 found = true; 64 self.files.push(path); 65 } 66 } 67 if found { 68 break; 69 } 70 } 71 } 72 self.files.pop() 73 }) 74 } 75} 76 77pub fn build_ignore_set<P: AsRef<Path>>( 78 ignore: &[String], 79 target: P, 80 unrestricted: bool, 81) -> Result<Gitignore, IgnoreError> { 82 let gitignore_path = target.as_ref().join(".gitignore"); 83 84 // Looks like GitignoreBuilder::new does not source globs 85 // within gitignore_path by default, we have to enforce that 86 // using GitignoreBuilder::add. Probably a bug in the ignore 87 // crate? 88 let mut gitignore = GitignoreBuilder::new(&gitignore_path); 89 90 // if we are to "restrict" aka "respect" .gitignore, then 91 // add globs from gitignore path as well 92 if !unrestricted { 93 gitignore.add(&gitignore_path); 94 95 // ignore .git by default, nobody cares about .git, i'm sure 96 gitignore.add_line(None, ".git")?; 97 } 98 99 for i in ignore { 100 gitignore.add_line(None, i.as_str())?; 101 } 102 103 gitignore.build() 104} 105 106pub fn walk_nix_files<P: AsRef<Path>>( 107 ignore: Gitignore, 108 target: P, 109) -> Result<impl Iterator<Item = PathBuf>, io::Error> { 110 let walker = dirs::Walker::new(target, ignore)?; 111 Ok(walker.filter(|path: &PathBuf| matches!(path.extension(), Some(e) if e == "nix"))) 112}