use std::fs;
use std::error::Error;
pub struct Config {
pub query: String,
pub filename: String,
pub ignore_case: bool,
}
impl Config {
pub fn new(mut iter: std::env::Args) -> Result<Config, &'static str> {
iter.next();
let query = match iter.next() {
None => return Err("No query string"),
Some(query) => query
};
let filename = match iter.next() {
None => return Err("No filename string"),
Some(filename) => filename
};
let mut config = Self {
query,
filename,
ignore_case: false,
};
for item in iter {
if item == "-I" || item == "--ignore-case" {
config.ignore_case = true;
break;
}
}
Ok(config)
}
}
pub fn run (config: Config)-> Result<(), Box<dyn Error>> {
let file_contents = fs::read_to_string(config.filename)?;
let matched = if config.ignore_case {
search_ignore_case(&config.query, &file_contents)
} else {
search(&config.query, &file_contents)
};
for item in matched {
println!("{}", item);
}
Ok(())
}
pub fn search<'a>(query: &str, content: &'a str) -> Vec<&'a str> {
content.lines().filter(|line| line.contains(query)).collect()
}
pub fn search_ignore_case<'a>(query: &str, content: &'a str) -> Vec<&'a str>{
content.lines().filter(|line| {
line.to_lowercase().contains(&query.to_lowercase())
}).collect()
}
#[cfg(test)]
mod unit_tests {
use crate::*;
#[test]
fn should_be_match() {
let contents = "\
other lines
hello world
HELLO WORLD
other lines
";
let expected = vec!["hello world"];
assert_eq!(expected, search("hello", contents));
}
#[test]
fn should_be_ignore_case_match() {
let contents = "\
other lines
hello world
HELLO WORLD
other lines
";
let expected = vec!["hello world", "HELLO WORLD"];
assert_eq!(expected, search_ignore_case("hello", contents));
}
}