use std::error::Error; use std::fs; use std::env; pub struct Config { pub query: String, pub filename: String, pub case_sensitive: bool, } impl Config { pub fn new(mut args: env::Args) -> Result { args.next(); let query = match args.next() { Some(arg) => arg, None => return Err("Didn't get a query string"), }; let filename = match args.next() { Some(arg) => arg, None => return Err("Didn't get a file name"), }; let case_sensitive = env::var("CASE_INSENSITIVE").is_err(); Ok(Config { query, filename, case_sensitive }) } } fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { contents .lines() .filter(|line| line.contains(query)) .collect() } fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { let query = query.to_lowercase(); contents .lines() .filter(|line| line.to_lowercase().contains(&query)) .collect() } pub fn run(config: Config) -> Result<(), Box> { let contents = fs::read_to_string(config.filename)?; let results = if config.case_sensitive { search(&config.query, &contents) } else { search_case_insensitive(&config.query, &contents) }; for line in results { println!("{}", line); } Ok(()) } #[cfg(test)] mod tests { use super::*; #[test] fn search_case_sensitive() { let query = "duct"; let contents = "\ Rust: safe, fast, productive. Pick three. Duct tape."; assert_eq!(vec!["safe, fast, productive."], search(query, contents)); } #[test] fn case_insensitive() { let query = "rUsT"; let contents = "\ Rust: safe, fast, productive. Pick three. Trust me."; assert_eq!( vec!["Rust:", "Trust me."], search_case_insensitive(query, contents) ); } #[test] fn run_valid_file() { assert!(run(Config { query: String::from("test"), filename: String::from("poem.txt"), case_sensitive: true, }).is_ok()); } #[test] fn run_invalid_file() { assert!(run(Config { query: String::from("test"), filename: String::from("134857320592.txt"), case_sensitive: true, }).is_err()); } }