lib.rs
1 use std::error::Error; 2 use std::fs; 3 use std::env; 4 5 pub struct Config { 6 pub query: String, 7 pub file_path: String, 8 pub ignore_case: bool, 9 } 10 11 impl Config { 12 pub fn build(args: &[String]) -> Result<Config, &'static str> { 13 if args.len() < 3 { 14 return Err("not enough arguments"); 15 } 16 17 let query = args[1].clone(); 18 let file_path = args[2].clone(); 19 20 let ignore_case = env::var("IGNORE_CASE").is_ok(); 21 22 Ok(Config { 23 query, 24 file_path, 25 ignore_case, 26 }) 27 } 28 } 29 30 pub fn run(config: Config) -> Result<(), Box<dyn Error>> { 31 let contents = fs::read_to_string(config.file_path)?; 32 33 let results = if config.ignore_case { 34 search_case_insensitive(&config.query, &contents) 35 } else { 36 search(&config.query, &contents) 37 }; 38 39 for line in results { 40 println!("{line}"); 41 } 42 43 Ok(()) 44 } 45 46 pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { 47 let mut results = Vec::new(); 48 49 for line in contents.lines() { 50 if line.contains(query) { 51 results.push(line); 52 } 53 } 54 55 results 56 } 57 58 pub fn search_case_insensitive<'a>(query: &str, 59 contents: &'a str 60 ) -> Vec<&'a str> { 61 let query = query.to_lowercase(); 62 let mut results = Vec::new(); 63 64 for line in contents.lines() { 65 if line.to_lowercase().contains(&query) { 66 results.push(line); 67 } 68 } 69 results 70 } 71 72 #[cfg(test)] 73 mod tests { 74 use super::*; 75 76 #[test] 77 fn case_sensitive() { 78 let query = "duct"; 79 let contents = "\ 80 Rust: 81 safe, fast, productive. 82 Pick three. 83 Duct tape."; 84 85 assert_eq!(vec!["safe, fast, productive."], search(query, contents)); 86 } 87 88 #[test] 89 fn case_insensitive() { 90 let query = "rUst"; 91 let contents = "\ 92 Rust: 93 safe, fast, productive. 94 Pick three. 95 Trust me."; 96 97 assert_eq!(vec!["Rust:", "Trust me."], 98 search_case_insensitive(query, contents) 99 ); 100 } 101 }