#![allow(unused)]
use anyhow::{Context, Result};
pub mod regex_feature;
use clap::Parser;
use std::io::{self, read_to_string, BufRead, BufReader, Write};
use std::{error::Error, fs::File, path::PathBuf};
#[derive(Parser, Debug)]
#[command(author, version, about)]
pub struct Config {
#[arg(short, long)]
pub query: String,
#[arg(short = 'r', long)]
pub regex_match: bool,
#[arg(short, long)]
pub file_path: PathBuf,
#[arg(short, long)]
pub ignore_case: bool,
#[arg(short = 'v', long)]
pub invert_match: bool,
}
pub fn run(config: Config) -> Result<()> {
if config.regex_match {
regex_feature::run_regex(config)
} else {
run_string(config)
}
}
pub fn run_string(config: Config) -> Result<()> {
let file = File::open(&config.file_path)
.with_context(|| format!("could not open the file `{:?}`", &config.file_path))?;
let reader = BufReader::new(file);
let contents = read_to_string(reader)
.with_context(|| format!("could not read the file `{:?}`", &config.file_path))?;
let results = if config.ignore_case {
search_case_insensitive(&config.query, &contents, config.invert_match)
} else {
search(&config.query, &contents, config.invert_match)
};
for line in results {
writeln!(io::BufWriter::new(io::stdout().lock()), "{line}");
}
Ok(())
}
pub fn search<'a>(query: &str, contents: &'a str, invert: bool) -> Vec<&'a str> {
let mut results = Vec::new();
if invert {
for line in contents.lines() {
if !line.contains(query) {
results.push(line);
}
}
} else {
for line in contents.lines() {
if line.contains(query) {
results.push(line);
}
}
}
results
}
pub fn search_case_insensitive<'a>(query: &str, contents: &'a str, invert: bool) -> Vec<&'a str> {
let query = query.to_lowercase();
let mut results = Vec::new();
if invert {
for line in contents.lines() {
if !(line.to_lowercase().contains(&query)) {
results.push(line);
}
}
} else {
for line in contents.lines() {
if line.to_lowercase().contains(&query) {
results.push(line);
}
}
}
results
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn case_sensitive() {
let query = "duct";
let contents = "\
Rust:
safe, fast, productive.
Duckt three.";
assert_eq!(
vec!["safe, fast, productive."],
search(query, contents, false)
);
}
#[test]
fn case_insensitive() {
let query = "rUsT";
let contents = "\
Rust:
safe, fast, productive.
Pick three.
Trust me.";
assert_eq!(
vec!["Rust:", "Trust me."],
search_case_insensitive(query, contents, false)
);
}
#[test]
fn invert_search() {
let query = "Duckt";
let contents = "\
safe, fast, productive.
Duckt three.
duckt";
assert_ne!(vec!["Duckt three."], search(query, contents, true));
assert_eq!(
vec!["safe, fast, productive."],
search_case_insensitive(query, contents, true)
);
}
}