minigrep/
lib.rs

1use std::error::Error;
2use std::env;
3use std::fs::File;
4use std::io::prelude::*;
5
6pub struct Config {
7    pub query: String,
8    pub filename: String,
9    pub case_sensitive: bool,
10}
11
12impl Config {
13    // &strだとスコープを出るとdropする
14    // もし&str使うならライフタイムの指定が必要
15    pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> {
16        args.next();
17
18        // if args.len() < 3 {
19        //     return Err("not enough arguments");
20        // }
21        let query = match args.next() {
22            // argは新しい変数で定義している
23            Some(arg) => arg,
24            None => return Err("Didn't get a query string"),
25        };
26        let filename = match args.next() {
27            Some(arg) => arg,
28            None => return Err("Didn't get a file name"),
29        };
30        let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
31
32        Ok(Config { query, filename, case_sensitive })
33    }
34}
35
36/// # コメントの説明
37/// "///"でマークダウンを、"//!"でモジュール全体の説明を記載できます
38pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
39    // let mut results = Vec::new();
40    // for line in contents.lines() {
41    //     if line.contains(query) {
42    //         results.push(line);
43    //     }
44    // };
45    // results
46    contents.lines()
47        .filter(|line| line.contains(query))
48        .collect()
49}
50
51pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
52    let query = query.to_lowercase();
53
54    // let mut results = Vec::new();
55    // for line in contents.lines() {
56    //     if line.to_lowercase().contains(&query) {
57    //         results.push(line);
58    //     }
59    // };
60    // results
61    contents.lines()
62        .filter(|line| line.to_lowercase().contains(&query))
63        .collect()
64}
65
66// errorは異なる型のエラー値を受け取れるとあるが、どのようにメモリ管理をしているのか?
67pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
68    let mut file = File::open(config.filename)?;
69    let mut contents = String::new();
70    file.read_to_string(&mut contents)?;
71    let results = if config.case_sensitive {
72        search(&config.query, &contents)
73    } else {
74        search_case_insensitive(&config.query, &contents)
75    };
76    for line in results {
77        println!("{}", line);
78    }
79    Ok(())
80}
81
82#[cfg(test)]
83mod test {
84    use super::*;
85
86    #[test]
87    fn case_sensitive() {
88        let query = "duct";
89        let contents = "\
90        Rust:
91safe, fast, productive.
92Pick three.
93Duct tape.";
94
95        assert_eq!(
96            vec!["safe, fast, productive."],
97            search(query, contents)
98        )
99    }
100
101    #[test]
102    fn case_insensitive() {
103        let query = "rUsT";
104        let contents = "\
105        Rust:
106safe, fast, productive.
107Pick three.
108Trust me.";
109
110        assert_eq!(
111            vec!["Rust:", "Trust me."],
112            search_case_insensitive(query, contents)
113        );
114    }
115}
116