minigrep_refine 0.1.0

A simple grep implementation from The Rust Programing Book. Practice only!
Documentation
//! minigrep_refine version
//! lib for minigrep_refine bin
//! Documentation test ~~~~~~
use std::env;
use std::error::Error;
use std::{fs, process};

pub struct Config {
    pub query: String,
    pub filename: String,
    pub case_sensitive:bool,
}
/*
提取参数解析过程到函数parse_config里
用元组结构获取query 和 filename 参数
将配置信息组织封装到一个struct里: 创建结构体,改造parse_config 和 let config =
将parse_config 函数 改成 Config struct的new函数
考虑 参数解析时可能的错误,比如参数数目不够时的处理,出错时panic
将错误处理放到调用方 让调用方去处理(返回错误Result)
main中调用Config::new时处理错误

从main中提取逻辑,提取一个run函数,传入config,run函数打开读取文件打印出来
文件打开错误处理放到main中让调用时来处理,返回Result。(返回错误,main中处理错误)

*/
impl Config {
    // pub fn new(args: &[String]) -> Result<Config, &str> {
    //     if args.len() < 3 {
    //         // panic!("not enough argument received");
    //         return Err("Not enough argument received");
    //     }
    //     let query = args[1].clone();
    //     let filename = args[2].clone();
    //     let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
    //     // Config{query,filename}
    //     Ok(Config { query, filename, case_sensitive:case_sensitive })
    // }
    pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> {

        // if args.len() < 3 {
        //     // panic!("not enough argument received");
        //     return Err("Not enough argument received");
        // }
        // let query = args[1].clone();
        // let filename = args[2].clone();

        args.next();
        let query = match args.next(){
            Some(q) => q,
            None =>  return Err("Query string not provided")
        };
        let filename = match args.next(){
            Some(q) => q,
            None =>  return Err("Filename not provided")
        };
        let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
        // Config{query,filename}
        Ok(Config { query, filename, case_sensitive})
    }
}

/// Open file and search for string line by line, print them if found
///
/// # Example
/// ```
/// let x = minigrep_refine::run(minigrep_refine::Config::new(std::env::args()).unwrap()).unwrap();
/// assert_eq!((),x); // how to run this tests when minigrep_refine is a package of a workspace?
/// // assert_eq!((),12);
/// ```
/// how to run this test
/// cargo run --package minigrep_refine  -- How minigrep_refine\poem.txt --test
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let contents = fs::read_to_string(config.filename)?;
    println!("With Contents:\n{}", contents);
    let results =  if config.case_sensitive{
        search(&config.query,&contents) // 没有分号
    }else {
        search_case_insensitive(&config.query,&contents)
    };

    for line in results{
        println!("{}",line);
    }
    Ok(())
}
// cargo test --package minigrep_refine --doc

/// search for string
///
/// # Examples
/// ```
/// let query = "How";
/// let contents = "abc\nbbb\nHow do you \n hasl \n how many\n";
/// let result = minigrep_refine::search(query,contents);
/// assert_eq!(result[0],"How do you ")
/// ```
pub fn search<'a>(query:&str,contents:&'a str) -> Vec<&'a str>{
    // let mut result:Vec<&str> = Vec::new();
    // for line in contents.lines(){
    //     if line.contains(query){
    //         result.push(line);
    //     }
    // }
    // result
    contents.lines()
        .filter(|x|x.contains(query))
        .collect()

}

fn search_case_insensitive<'a>(query:&str,contents:&'a str) -> Vec<&'a str>{
    // let query = query.to_lowercase();
    // let mut result:Vec<&str> = Vec::new();
    // for line in contents.lines(){
    //     if line.to_lowercase().contains(&query){ //索引后面需要用模式匹配 The pattern can be a &str, char, a slice of chars, or a function or closure that determines if a character matches.
    //         result.push(line);
    //     }
    // }
    // result
    contents.lines()
        .filter(|x|x.to_lowercase().contains(&query.to_lowercase()))
        .collect()
}

#[cfg(test)]
mod tests{
    use std::env::Args;
    use super::*;
    #[test]
    fn new_config(){
        // how to test
        // cargo test --package minigrep_refine -- How minigrep_refine\poem.txt tests
        // let v = vec!["minigrep".to_string(),"how".to_string(),"how.txt".to_string()].into_iter();
        let n = Config::new(std::env::args()).expect("test failed at new config");
        assert_eq!("How",n.query);
        assert_eq!("minigrep_refine\\poem.txt",n.filename);
    }

    #[test]
    fn one_result(){
        let query = "How".to_string();
        let contents = "I'm nobody! Who are you?\nHow do you do\n you like me \n i like you".to_string();

        assert_eq!(vec!["How do you do"],search(&query,&contents));
    }
    #[test]
    fn search_case_insensitive_test(){
        let query = "how".to_string();
        let contents = "I'm nobody! Who are you?\nHow do you do\n you like me \n i like you".to_string();
        assert_eq!(vec!["How do you do"],search_case_insensitive(&query,&contents));
    }
}