Ignore case and allowing the user to specify a path
Also ran rustfmt over the code.
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
extern crate clap;
|
extern crate clap;
|
||||||
use clap::{Arg, App};
|
use clap::{App, Arg};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub pattern: String,
|
pub pattern: String,
|
||||||
pub ignore_case: bool
|
pub ignore_case: bool,
|
||||||
|
pub path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -12,19 +13,29 @@ impl Config {
|
|||||||
let matches = App::new("Rack")
|
let matches = App::new("Rack")
|
||||||
.version("0.1")
|
.version("0.1")
|
||||||
.about("Like Ack but in Rust")
|
.about("Like Ack but in Rust")
|
||||||
.arg(Arg::with_name("ignore case")
|
.arg(
|
||||||
|
Arg::with_name("ignore case")
|
||||||
.short("i")
|
.short("i")
|
||||||
.help("Ignore case when looking for matches"))
|
.help("Ignore case when looking for matches"),
|
||||||
.arg(Arg::with_name("pattern")
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("pattern")
|
||||||
.help("The pattern you're looking for")
|
.help("The pattern you're looking for")
|
||||||
.index(1)
|
.index(1)
|
||||||
.required(true))
|
.required(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("path")
|
||||||
|
.help("Path to search in")
|
||||||
|
.index(2)
|
||||||
|
.default_value("."),
|
||||||
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
Config {
|
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()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
72
src/main.rs
72
src/main.rs
@@ -1,5 +1,6 @@
|
|||||||
|
use std::fs::{self, File};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::fs::{self};
|
use std::io::prelude::*;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
extern crate term;
|
extern crate term;
|
||||||
@@ -9,13 +10,20 @@ mod config;
|
|||||||
fn main() {
|
fn main() {
|
||||||
let config = config::Config::from_args();
|
let config = config::Config::from_args();
|
||||||
|
|
||||||
let path = Path::new(".");
|
let search_path = &config.path;
|
||||||
|
let path = match search_path.starts_with('/') {
|
||||||
|
true => Path::new(search_path),
|
||||||
|
false => {
|
||||||
|
let p: &'static str = Box::leak(Box::new(String::from("./") + search_path));
|
||||||
|
Path::new(p)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let results = traverse_dir(&config, path, |config, path| check_file(config, path));
|
let results = traverse_dir(&config, path, |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(&matches),
|
Result::Ok(matches) => print_report(&matches),
|
||||||
Result::Err(msg) => println!("Error while parsing: {}", msg)
|
Result::Err(msg) => println!("Error while parsing: {}", msg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +63,7 @@ fn traverse_dir(
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct FileMatch {
|
struct FileMatch {
|
||||||
file_name: String,
|
file_name: String,
|
||||||
matches: Vec<Match>
|
matches: Vec<Match>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileMatch {
|
impl FileMatch {
|
||||||
@@ -73,9 +81,15 @@ impl FileMatch {
|
|||||||
|
|
||||||
let mut last_position = 0;
|
let mut last_position = 0;
|
||||||
for (idx, match_index) in mat.match_indexes.iter().enumerate() {
|
for (idx, match_index) in mat.match_indexes.iter().enumerate() {
|
||||||
print!("{}", mat.text.get(last_position..match_index.start).unwrap());
|
print!(
|
||||||
|
"{}",
|
||||||
|
mat.text.get(last_position..match_index.start).unwrap()
|
||||||
|
);
|
||||||
term.fg(term::color::BRIGHT_RED).unwrap();
|
term.fg(term::color::BRIGHT_RED).unwrap();
|
||||||
print!("{}", mat.text.get(match_index.start..match_index.end).unwrap());
|
print!(
|
||||||
|
"{}",
|
||||||
|
mat.text.get(match_index.start..match_index.end).unwrap()
|
||||||
|
);
|
||||||
term.reset().unwrap();
|
term.reset().unwrap();
|
||||||
|
|
||||||
if idx == mat.match_indexes.len() - 1 {
|
if idx == mat.match_indexes.len() - 1 {
|
||||||
@@ -94,43 +108,55 @@ impl FileMatch {
|
|||||||
struct Match {
|
struct Match {
|
||||||
line_number: usize,
|
line_number: usize,
|
||||||
match_indexes: Vec<MatchIndex>,
|
match_indexes: Vec<MatchIndex>,
|
||||||
text: String
|
text: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct MatchIndex {
|
struct MatchIndex {
|
||||||
start: usize,
|
start: usize,
|
||||||
end: usize
|
end: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_file(config: &config::Config, path: &Path) -> io::Result<FileMatch> {
|
fn check_file(config: &config::Config, path: &Path) -> io::Result<FileMatch> {
|
||||||
let pattern = &config.pattern;
|
let mut pattern = config.pattern.clone();
|
||||||
|
|
||||||
// TODO: Use BufRead and read_line instead of reading the entire file into memory.
|
let file = File::open(&path)?;
|
||||||
let content = fs::read_to_string(&path)?;
|
let file = io::BufReader::new(file);
|
||||||
let mut matches = Vec::new();
|
let mut matches = Vec::new();
|
||||||
for (number, line) in content.lines().enumerate() {
|
|
||||||
if line.contains(pattern) {
|
for (number, line) in file.lines().enumerate() {
|
||||||
|
let mut line = line?;
|
||||||
|
let orig_line = line.clone();
|
||||||
|
if config.ignore_case {
|
||||||
|
line = line.to_lowercase();
|
||||||
|
pattern = pattern.to_lowercase();
|
||||||
|
}
|
||||||
|
|
||||||
|
if line.contains(&pattern) {
|
||||||
let mut indexes = Vec::<MatchIndex>::new();
|
let mut indexes = Vec::<MatchIndex>::new();
|
||||||
for m in line.trim().match_indices(pattern) {
|
for m in line.trim().match_indices(&pattern) {
|
||||||
indexes.push(MatchIndex { start: m.0, end: m.0 + pattern.len() });
|
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 });
|
matches.push(Match {
|
||||||
|
line_number: number + 1,
|
||||||
|
text: orig_line.trim().to_string(),
|
||||||
|
match_indexes: indexes,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(FileMatch { file_name: path.to_str().unwrap().to_string(), matches: matches })
|
Ok(FileMatch {
|
||||||
|
file_name: path.to_str().unwrap().trim_start_matches("./").to_string(),
|
||||||
|
matches: matches,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_report(matches: &Vec<FileMatch>) {
|
fn print_report(matches: &Vec<FileMatch>) {
|
||||||
for file in matches {
|
for file in matches {
|
||||||
file.print()
|
file.print()
|
||||||
// println!("{}:", file.file_name);
|
|
||||||
// for mat in &file.matches {
|
|
||||||
// println!("{}", mat.text);
|
|
||||||
// }
|
|
||||||
// println!();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user