Add basic gitignore functionality

No support for wildcards and exact matches might screw up nested
directories. The Globbing functionality seems like its going to be a
pain to properly match up.
This commit is contained in:
Cameron Cordes
2020-05-20 15:38:26 -04:00
parent ee3e61b76d
commit 1229c05d8d
4 changed files with 60 additions and 9 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
# comment
/target /target

View File

@@ -6,6 +6,7 @@ pub struct Config {
pub pattern: String, pub pattern: String,
pub ignore_case: bool, pub ignore_case: bool,
pub path: String, pub path: String,
pub use_gitignore: bool,
} }
impl Config { impl Config {
@@ -28,7 +29,12 @@ impl Config {
Arg::with_name("path") Arg::with_name("path")
.help("Path to search in") .help("Path to search in")
.index(2) .index(2)
.default_value("."), .default_value(""),
)
.arg(
Arg::with_name("gitignore")
.long("no-gitignore")
.help("Include results that are specified in .gitignore"),
) )
.get_matches(); .get_matches();
@@ -36,6 +42,7 @@ impl Config {
pattern: String::from(matches.value_of("pattern").unwrap()), pattern: String::from(matches.value_of("pattern").unwrap()),
ignore_case: matches.is_present("ignore case"), ignore_case: matches.is_present("ignore case"),
path: String::from(matches.value_of("path").unwrap()), path: String::from(matches.value_of("path").unwrap()),
use_gitignore: !matches.is_present("gitignore"),
} }
} }
} }

View File

@@ -1,6 +1,6 @@
use rack::config::Config; use rack::config::Config;
use rack::filematch::*; use rack::filematch::*;
use std::fs::{self, File}; use std::fs::{self, DirEntry, File};
use std::io; use std::io;
use std::io::prelude::*; use std::io::prelude::*;
use std::path::Path; use std::path::Path;
@@ -19,7 +19,9 @@ fn main() {
} }
}; };
let results = traverse_dir(&config, path, |config, path| check_file(config, path)); let results = traverse_dir(&config, path, Option::None, |config, path| {
check_file(config, path)
});
match results { match results {
Result::Ok(matches) if matches.len() == 0 => println!("No matches :("), Result::Ok(matches) if matches.len() == 0 => println!("No matches :("),
Result::Ok(matches) => print_report(&config, &matches), Result::Ok(matches) => print_report(&config, &matches),
@@ -30,15 +32,46 @@ fn main() {
fn traverse_dir( fn traverse_dir(
config: &Config, config: &Config,
dir: &Path, dir: &Path,
gitignore_entries: Option<Vec<String>>,
operation: fn(&Config, &Path) -> io::Result<FileMatch>, operation: fn(&Config, &Path) -> io::Result<FileMatch>,
) -> io::Result<Vec<FileMatch>> { ) -> io::Result<Vec<FileMatch>> {
let mut matches: Vec<FileMatch> = Vec::new(); let mut matches: Vec<FileMatch> = Vec::new();
for entry in fs::read_dir(dir)? { let entries = fs::read_dir(dir)?.collect::<Vec<io::Result<DirEntry>>>();
let gitignore: Option<&DirEntry> = entries
.iter()
.filter_map(|entry| entry.as_ref().ok())
.find(|entry| entry.path().file_name().unwrap() == ".gitignore");
let gitignore = match gitignore {
Some(g) => {
let file = io::BufReader::new(File::open(g.path())?);
file.lines()
.map(|line| line.unwrap().trim().to_owned())
.filter(|line| line.len() > 0 && !line.starts_with('#'))
.collect::<Vec<String>>()
}
None => gitignore_entries.unwrap_or(Vec::new()),
};
for entry in entries {
let entry = entry?; let entry = entry?;
let file_type = entry.file_type()?; let file_type = entry.file_type()?;
let path = entry.path().to_str().unwrap().to_owned();
if file_type.is_dir() { if file_type.is_dir() {
let mut results = traverse_dir(&config, &entry.path(), operation)?; if config.use_gitignore {
let should_ignore = gitignore.iter().find(|&line| path.contains(&line.clone()));
if should_ignore.unwrap_or(&String::from("")).len() > 0
|| entry.path().to_str().unwrap().contains("/.git")
{
continue;
}
}
// TODO: Should borrow gitignore instead of cloning..
let mut results =
traverse_dir(&config, &entry.path(), Some(gitignore.clone()), operation)?;
let has_results = results let has_results = results
.iter() .iter()
.filter({ |mat| mat.matches.len() > 0 }) .filter({ |mat| mat.matches.len() > 0 })
@@ -49,6 +82,15 @@ fn traverse_dir(
matches.append(&mut results); matches.append(&mut results);
} }
} else { } else {
if config.use_gitignore {
let should_ignore = gitignore.iter().find(|&line| path.contains(&line.clone()));
if should_ignore.unwrap_or(&String::from("")).len() > 0
|| entry.path().to_str().unwrap().contains("/.git")
{
continue;
}
}
let result = operation(&config, &entry.path()); let result = operation(&config, &entry.path());
match result { match result {
Result::Ok(result) if result.matches.len() > 0 => matches.push(result), Result::Ok(result) if result.matches.len() > 0 => matches.push(result),

View File

@@ -12,9 +12,9 @@ pub struct ColorPrinter<'a> {
pub file_match: &'a FileMatch, pub file_match: &'a FileMatch,
} }
struct SimplePrinter<'a> { pub struct SimplePrinter<'a> {
config: &'a Config, pub config: &'a Config,
file_match: &'a FileMatch, pub file_match: &'a FileMatch,
} }
impl<'a> Printer for SimplePrinter<'a> { impl<'a> Printer for SimplePrinter<'a> {
@@ -71,6 +71,7 @@ impl<'a> Printer for ColorPrinter<'a> {
}; };
let mut term = term::stdout().unwrap(); let mut term = term::stdout().unwrap();
term.fg(term::color::YELLOW).unwrap(); term.fg(term::color::YELLOW).unwrap();
term.attr(term::Attr::Bold).unwrap(); term.attr(term::Attr::Bold).unwrap();
simple_printer.print_file_name(); simple_printer.print_file_name();