quewuigrep/
lib.rs

1use std::{env, error::Error, fs};
2
3/// Runs the search based on the provided configuration.
4/// 
5/// # Arguments
6/// 
7/// * `config` - A `Config` struct containing the query, filename, and case sensitivity flag.
8/// 
9/// # Returns
10/// 
11/// * `Result<(), Box<dyn Error>>` - Returns `Ok(())` if successful, or an error if something goes wrong.
12/// 
13/// # Examples
14/// 
15/// ```
16/// use quewuigrep::{Config, run};
17/// use std::env;
18/// 
19/// let args: Vec<String> = vec!["program".into(), "query".into(), "filename.txt".into()];
20/// let config = Config::new(&args).unwrap();
21/// run(config).unwrap();
22/// ```
23pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
24    let contents = fs::read_to_string(config.filename)?;
25
26    let results = if config.case_sensitive {
27        search(&config.query, &contents)
28    } else {
29        search_case_insensitive(&config.query, &contents)
30    };
31    for line in results {
32        println!("{}", line);
33    }
34    Ok(())
35}
36
37/// Holds the configuration for the search.
38/// 
39/// # Fields
40/// 
41/// * `query` - The string to search for.
42/// * `filename` - The name of the file to search in.
43/// * `case_sensitive` - A flag indicating whether the search should be case-sensitive.
44/// 
45/// # Examples
46/// 
47/// ```
48/// use quewuigrep::Config;
49/// use std::env;
50/// 
51/// let args: Vec<String> = vec!["program".into(), "query".into(), "filename.txt".into()];
52/// let config = Config::new(&args).unwrap();
53/// assert_eq!(config.query, "query");
54/// assert_eq!(config.filename, "filename.txt");
55/// assert!(config.case_sensitive);
56/// ```
57pub struct Config {
58    pub query: String,
59    pub filename: String,
60    pub case_sensitive: bool,
61}
62
63impl Config {
64    /// Creates a new `Config` instance from command-line arguments.
65    /// 
66    /// # Arguments
67    /// 
68    /// * `args` - An iterator over the command-line arguments.
69    /// 
70    /// # Returns
71    /// 
72    /// * `Result<Config, &'static str>` - Returns a `Config` instance if successful, or an error message if not.
73    /// 
74    /// # Examples
75    /// 
76    /// ```
77    /// use quewuigrep::Config;
78    /// use std::env;
79    /// 
80    /// let args: Vec<String> = vec!["program".into(), "query".into(), "filename.txt".into()];
81    /// let config = Config::new(&args).unwrap();
82    /// assert_eq!(config.query, "query");
83    /// assert_eq!(config.filename, "filename.txt");
84    /// assert!(config.case_sensitive);
85    /// ```
86    pub fn new(mut args: env::Args) -> Result<Config, &'static str> {
87        args.next();
88
89        let query = match args.next() {
90            Some(arg) => arg,
91            None => return Err("Didn't get a query string!"),
92        };
93
94        let filename = match args.next() {
95            Some(arg) => arg,
96            None => return Err("Didn't get a filename!"),
97        };
98
99        let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
100
101        Ok(Config {
102            query,
103            filename,
104            case_sensitive,
105        })
106    }
107}
108
109/// Searches for the query string in the contents, case-sensitive.
110/// 
111/// # Arguments
112/// 
113/// * `query` - The string to search for.
114/// * `contents` - The contents of the file to search in.
115/// 
116/// # Returns
117/// 
118/// * `Vec<&str>` - A vector of lines that contain the query string.
119/// 
120/// # Examples
121/// 
122/// ```
123/// use quewuigrep::search;
124/// 
125/// let query = "duct";
126/// let contents = "\
127/// Rust:
128/// safe, fast, productive.
129/// Pick three.
130/// Duct tape.";
131/// 
132/// let result = search(query, contents);
133/// assert_eq!(result, vec!["safe, fast, productive."]);
134/// ```
135pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
136    contents
137        .lines()
138        .filter(|line| line.contains(query))
139        .collect()
140}
141
142/// Searches for the query string in the contents, case-insensitive.
143/// 
144/// # Arguments
145/// 
146/// * `query` - The string to search for.
147/// * `contents` - The contents of the file to search in.
148/// 
149/// # Returns
150/// 
151/// * `Vec<&str>` - A vector of lines that contain the query string, ignoring case.
152/// 
153/// # Examples
154/// 
155/// ```
156/// use quewuigrep::search_case_insensitive;
157/// 
158/// let query = "rUsT";
159/// let contents = "\
160/// Rust:
161/// safe, fast, productive.
162/// Pick three.
163/// Trust me.";
164/// 
165/// let result = search_case_insensitive(query, contents);
166/// assert_eq!(result, vec!["Rust:", "Trust me."]);
167/// ```
168pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
169    let query = query.to_lowercase();
170    let mut results = Vec::new();
171
172    for line in contents.lines() {
173        if line.to_lowercase().contains(&query) {
174            results.push(line);
175        }
176    }
177    results
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    #[test]
185    fn case_sensitive() {
186        let query = "duct";
187        let contents = "\
188Rust:
189safe, fast, productive.
190Pick three.
191Duct tape. ";
192
193        assert_eq!(vec!["safe, fast, productive."], search(query, contents));
194    }
195
196    #[test]
197    fn case_insensitive() {
198        let query = "rUsT";
199        let contents = "\
200Rust:
201safe, fast, productive.
202Pick three.
203Trust me.";
204
205        assert_eq!(
206            vec!["Rust:", "Trust me."],
207            search_case_insensitive(query, contents)
208        );
209    }
210}