use std::error::Error; use std::{env, fs}; pub struct Config { pub query: String, pub file_path: String, pub ignore_case: bool, } impl Config { fn new(args: &[String]) -> Config { if args.len() < 3 { panic!("not enough arguments"); } // let query = &args[1]; // let file_path = &args[2]; // (query, file_path) // (&args[1], &args[2]) // In this case while learning rust, it is okay // to use clone() here while not managing lifetimes // and such. // // For rush, I will absolutely want to mess with // lifetimes and references for speed. // Gonna make this fast af // https://www.youtube.com/watch?v=y-5JFBEoU9c Config { query: args[1].clone(), file_path: args[2].clone(), ignore_case: false, } } // pub fn build(args: &[String]) -> Result { // if args.len() < 3 { // return Err("not enough arguments"); // } // Ok(Config { // query: args[1].clone(), // file_path: args[2].clone(), // ignore_case: env::var("IGNORE_CASE").is_ok(), // }) // } pub fn build(mut args: impl Iterator) -> Result { args.next(); let query = match args.next() { Some(arg) => arg, None => return Err("Didn't get a query string"), }; let file_path = match args.next() { Some(arg) => arg, None => return Err("Didn't get a file path"), }; let ignore_case = env::var("IGNORE_CASE").is_ok(); Ok(Config { query, file_path, ignore_case, }) } } pub fn run(config: Config) -> Result<(), Box> { // Best to group configuration variables such as query, file_path, // and contents into one structure. let contents = fs::read_to_string(config.file_path)?; let results = if config.ignore_case { search_case_insensitive(&config.query, &contents) } else { search(&config.query, &contents) }; for line in search(&config.query, &contents) { println!("{line}"); } // println!("With text:\n{contents}"); Ok(()) } pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { contents .lines() .filter(|line| line.contains(query)) .collect() // let mut results = Vec::new(); // for line in contents.lines() { // if line.contains(query) { // results.push(line); // } // } // results } pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { let query = query.to_lowercase(); let mut results = Vec::new(); for line in contents.lines() { if line.to_lowercase().contains(&query) { results.push(line); } } results } #[cfg(test)] mod tests { use super::*; #[test] fn one_result() { let query = "duct"; let contents = "\ Rust safe, fast, productive. Pick three."; assert_eq!(vec!["safe, fast, productive."], search(query, contents)); } #[test] fn 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) ); } }