xer_minigrep 0.0.0

minigrep is a minimal version of grep as shown in the rust book.
Documentation
//! # Minigrep
//! minigrep is a minimal version of grep as shown in the [rust book](https://doc.rust-lang.org/book/).
//!
//! This is the documentation for the library crate part of minigrep.

use std::env;
use std::error::Error;
use std::fs;

#[derive(Debug)]
pub struct Config {
    pub query: String,
    pub file_path: String,
    pub ignore_case: bool,
}

impl Config {
    /// Creates a `Config` from the arguments which could come from
    /// `std::env::args()` or any other `String` iterator.
    ///
    /// # Examples
    ///
    /// ```
    /// use xer_minigrep::Config;
    ///
    /// fn main() {
    ///		let args = vec!(String::from("./minigrep"), String::from("body"), String::from("poem.txt"));
    /// 	let iter = args.into_iter();
    /// 	let config = Config::build(iter).unwrap();
    /// 	println!("{:?}", config);
    /// }
    /// ```
    pub fn build(mut args: impl Iterator<Item = String>) -> Result<Self, String> {
        args.next()
            .ok_or(String::from("Program name argument missing"))?;

        // TODO: `unwrap_or_else` with closure is shorter
        let query = match args.next() {
            Some(query) => query,
            None => return Err(String::from("Didn't get query string argument")),
        };
        let file_path = match args.next() {
            Some(file_path) => file_path,
            None => return Err(String::from("Didn't get file path string argument")),
        };

        let ignore_case = env::var("IGNORE_CASE").is_ok();
        Ok(Self {
            query,
            file_path,
            ignore_case,
        })
    }
}

/// Performs a search according to what values are in the passed in `Config`,
/// which could mean case sensitive search, case insensitive search, etc.
///
/// # Examples
///
/// ```
/// use xer_minigrep::Config;
///
/// fn main() {
///		let args = vec!(String::from("./minigrep"), String::from("body"), String::from("poem.txt"));
/// 	let iter = args.into_iter();
/// 	let config = Config::build(iter).unwrap();
/// 	xer_minigrep::run(config).unwrap();
/// }
/// ```
///
/// # Errors
///
/// This function will return a `Result` if it failed to read the file
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
    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 results {
        println!("{line}");
    }
    Ok(())
}

/// Returns a vector of references to `str` where each `str` is a line that
/// matched case-sensitively a search for `query` in `contents`.
///
/// # Examples
///
/// ```
/// fn main() {
/// 	let contents = "\
/// Rust:
/// safe, fast, productive.
/// Pick three.
/// Trust Me.";
/// 	let results = xer_minigrep::search("rust", contents);
/// 	assert_eq!(vec!("Trust Me."), results);
/// }
/// ```
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    contents
        .lines()
        .filter(|line| line.contains(query))
        .collect()
}

/// Returns a vector of references to `str` where each `str` is a line that
/// matched case-insensitively a search for `query` in `contents`.
///
/// # Examples
///
/// ```
/// fn main() {
/// 	let contents = "\
/// Rust:
/// safe, fast, productive.
/// Pick three.
/// Trust Me.";
/// 	let results = xer_minigrep::search_case_insensitive("rust", contents);
/// 	assert_eq!(vec!("Rust:", "Trust Me."), results);
/// }
/// ```
pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    contents
        .lines()
        .filter(|line| line.to_lowercase().contains(&query.to_lowercase()))
        .collect()
}

#[cfg(test)]
mod lib_test;