use std::env;
use std::error::Error;
use std::fs;
pub struct ConfigArgs {
pub query: String,
pub file_path: String,
pub ignore_case: bool,
}
impl ConfigArgs {
pub fn build(mut args: impl Iterator<Item = String>) -> Result<Self, &'static str> {
args.next();
Ok(ConfigArgs {
query: args.next().ok_or("query not found")?,
file_path: args.next().ok_or("file path not found")?,
ignore_case: env::var("IGNORE_CASE").is_ok(),
})
}
}
pub fn run(config: &ConfigArgs) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(&config.file_path)?;
if config.ignore_case {
for lines in search_i(&config.query, &contents) {
println!("{lines}");
}
} else {
for lines in search(&config.query, &contents) {
println!("{lines}");
}
}
Ok(())
}
fn search<'a>(query: &str, content: &'a str) -> Vec<&'a str> {
content
.lines()
.filter(|&line| line.contains(query))
.collect()
}
fn search_i<'a>(query: &str, content: &'a str) -> Vec<&'a str> {
let query = query.to_lowercase();
content
.lines()
.filter(|&line| line.to_lowercase().contains(&query))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn case_sensitive_search() {
let needle = "more";
let haystack = "\
Once more into the fray.
This time even MORE deep.
Into the last good fight I'll ever know.
Live and die on this day.
Live and die on this day.";
assert_eq!(vec!["Once more into the fray."], search(needle, haystack));
}
#[test]
fn case_insensitive_search() {
let needle = "lIvE";
let haystack = "\
Once more into the fray.
Into the last good fight I'll ever know.
Live and die on this day.
Live and die on this day.
I am alive.";
assert_eq!(
vec![
"Live and die on this day.",
"Live and die on this day.",
"I am alive."
],
search_i(needle, haystack)
);
}
}