diff --git a/Cargo.lock b/Cargo.lock index a5bf449..1fe17ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,6 +9,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + [[package]] name = "atty" version = "0.2.14" @@ -20,12 +32,41 @@ dependencies = [ "winapi", ] +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "clap" version = "2.33.0" @@ -41,6 +82,56 @@ dependencies = [ "vec_map", ] +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +dependencies = [ + "cfg-if", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" +dependencies = [ + "cfg-if", + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "hermit-abi" version = "0.1.12" @@ -50,6 +141,12 @@ dependencies = [ "libc", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.69" @@ -61,6 +158,36 @@ name = "rack" version = "0.1.0" dependencies = [ "clap", + "term", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + +[[package]] +name = "redox_users" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + +[[package]] +name = "rust-argon2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +dependencies = [ + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", ] [[package]] @@ -69,6 +196,16 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "term" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" +dependencies = [ + "dirs", + "winapi", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -90,6 +227,12 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "winapi" version = "0.3.8" diff --git a/Cargo.toml b/Cargo.toml index aaba8a0..56f8140 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,6 @@ version = "0.1.0" authors = ["Cameron Cordes "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] clap = "2.33.0" +term = "0.6.1" diff --git a/src/main.rs b/src/main.rs index 51be079..f57658e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,31 +1,130 @@ -use std::env; use std::io; use std::fs::{self}; use std::path::Path; +extern crate term; + mod config; fn main() { let config = config::Config::from_args(); - println!("{:?}", config); let path = Path::new("."); - traverse_dir(path); + let results = traverse_dir(&config, path, |config, path| check_file(config, path)); + match results { + Result::Ok(matches) if matches.len() == 0 => println!("No matches :("), + Result::Ok(matches) => print_report(&matches), + Result::Err(msg) => println!("Error while parsing: {}", msg) + } } -fn parse_args() { +fn traverse_dir(config: &config::Config, dir: &Path, operation: fn(&config::Config, &Path) -> io::Result) + -> io::Result> { + + let mut matches: Vec = Vec::new(); + for entry in fs::read_dir(dir)? { + let entry = entry?; + let file_type = entry.file_type()?; + + if file_type.is_dir() { + let results = traverse_dir(&config, &entry.path(), operation)?; + for mat in results.as_slice() { + if mat.matches.len() > 0 { + matches.append(&mut results.clone()); + } + } + } else { + let result = operation(&config, &entry.path()); + match result { + Result::Ok(result) if result.matches.len() > 0 => matches.push(result), + _ => () + } + } + } + + Ok(matches) + } + +#[derive(Debug, Clone)] +struct FileMatch { + file_name: String, + matches: Vec } -fn traverse_dir(dir: &Path) -> io::Result<()> { - for entry in fs::read_dir(dir)? { - let entry = entry?; - let file_type = entry.file_type()?; +impl FileMatch { + fn print(&self) { + let mut term = term::stdout().unwrap(); + term.fg(term::color::YELLOW).unwrap(); + term.attr(term::Attr::Bold).unwrap(); + println!("{}:", self.file_name); + term.reset().unwrap(); - if file_type.is_dir() { - traverse_dir(&entry.path()); + for mat in &self.matches { + term.attr(term::Attr::Bold).unwrap(); + print!("{}: ", mat.line_number); + term.reset().unwrap(); + + let mut last_position = 0; + for (idx, match_index) in mat.match_indexes.iter().enumerate() { + print!("{}", mat.text.get(last_position..match_index.start).unwrap()); + term.fg(term::color::BRIGHT_RED).unwrap(); + print!("{}", mat.text.get(match_index.start..match_index.end).unwrap()); + term.reset().unwrap(); + + if idx == mat.match_indexes.len() -1 { + println!("{}", mat.text.get(match_index.end..mat.text.len()).unwrap()); + last_position = 0; + } else { + last_position = match_index.end; + } + } + } + println!(); + } +} + +#[derive(Debug, Clone)] +struct Match { + line_number: usize, + match_indexes: Vec, + text: String +} + +#[derive(Debug, Clone)] +struct MatchIndex { + start: usize, + end: usize +} + +fn check_file(config: &config::Config, path: &Path) -> io::Result { + let pattern = &config.pattern; + + // TODO: Use BufRead and read_line instead of reading the entire file into memory. + let content = fs::read_to_string(&path)?; + let mut matches = Vec::new(); + for (number, line) in content.lines().enumerate() { + if line.contains(pattern) { + let mut indexes = Vec::::new(); + for m in line.trim().match_indices(pattern) { + indexes.push(MatchIndex { start: m.0, end: m.0 + pattern.len() }); + } + + matches.push(Match { line_number: number + 1, text: line.trim().to_string(), match_indexes: indexes }); } } - Ok(()) + Ok(FileMatch { file_name: path.to_str().unwrap().to_string(), matches: matches }) } + +fn print_report(matches: &Vec) { + for file in matches { + file.print() + // println!("{}:", file.file_name); + // for mat in &file.matches { + // println!("{}", mat.text); + // } + // println!(); + } +} +