Skip to content

Commit

Permalink
nit: Strict clippy (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
miampf authored Jan 21, 2024
1 parent 997c5f5 commit 5b788c7
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 59 deletions.
13 changes: 13 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
name = "tag"
version = "0.1.0"
edition = "2021"
description = "Search for local text files with a simple tagging system."
license = "MIT"
repository = "https://github.com/miampf/tag"
keywords = ["organization", "tagging", "plaintext", "searching"]
categories = ["command-line-utilities", "filesystem"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand All @@ -13,3 +18,11 @@ pest = "2.7.6"
pest_derive = "2.7.6"
textwrap = "0.16.0"
walkdir = "2.4.0"

[lints.clippy]
pedantic = "warn"
nursery = "warn"
suspicious = "warn"
complexity = "deny"
perf = "deny"
style = "deny"
30 changes: 14 additions & 16 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::{path::PathBuf, process::Command};
use std::{path::Path, process::Command};

use colored::Colorize;
use pest::Parser;
use tag::{
parsers::query::{construct_query_ast, evaluate_ast, QueryParser, Rule},
parsers::searchquery::{construct_query_ast, evaluate_ast, QueryParser, Rule},
search::get_tags_from_files,
};

Expand Down Expand Up @@ -39,13 +39,13 @@ mod cli {
}

impl Cli {
pub fn new_and_parse() -> Cli {
Cli::parse()
pub fn new_and_parse() -> Self {
Self::parse()
}
}
}

fn execute_command_on_file(path: PathBuf, command: String) -> String {
fn execute_command_on_file(path: &Path, command: &str) -> String {
let command = command.replace("#FILE#", path.to_str().unwrap());

let output = if cfg!(target_os = "windows") {
Expand Down Expand Up @@ -78,7 +78,7 @@ fn execute_command_on_file(path: PathBuf, command: String) -> String {
output_string.unwrap().to_string()
}

fn execute_filter_command_on_file(path: PathBuf, command: String) -> bool {
fn execute_filter_command_on_file(path: &Path, command: &str) -> bool {
let command = command.replace("#FILE#", path.to_str().unwrap());

let output = if cfg!(target_os = "windows") {
Expand Down Expand Up @@ -121,10 +121,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

let query = query.unwrap();

for file in file_index.iter() {
for file in file_index {
let ast = construct_query_ast(
query.clone().next().unwrap().into_inner(),
file.tags.iter().map(|tag| tag.as_str()).collect(),
&file.tags.iter().map(std::string::String::as_str).collect(),
);

// skip the file if tags don't match query
Expand All @@ -134,20 +134,18 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

// skip the file if filter command is unsuccessful
if args.filter_command.is_some()
&& !execute_filter_command_on_file(
file.path.clone(),
args.filter_command.clone().unwrap(),
)
&& !execute_filter_command_on_file(&file.path, &args.filter_command.clone().unwrap())
{
continue;
}

println!("{}", file.path.display().to_string().green());

let mut output = String::new();
if args.command.is_some() {
output = execute_command_on_file(file.path.clone(), args.command.clone().unwrap());
}
let output = if args.command.is_some() {
execute_command_on_file(&file.path, &args.command.clone().unwrap())
} else {
String::new()
};

if !args.silent {
println!("\t{}", format!("tags: {:?}", file.tags).blue());
Expand Down
73 changes: 38 additions & 35 deletions src/parsers.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
pub mod tagline {
pub mod onfile {
use pest_derive::Parser;

#[derive(Parser)]
#[grammar = "tagline.pest"]
/// TaglineParser is responsible for parsing the taglines at the start of each searched file.
/// `TaglineParser` is responsible for parsing the taglines at the start of each searched file.
/// The relevant rule is `tagline`.
pub struct TaglineParser;
}

pub mod query {
pub mod searchquery {
use pest::{iterators::Pairs, pratt_parser::PrattParser};
use pest_derive::Parser;

Expand All @@ -25,16 +25,16 @@ pub mod query {
}

/// Op is an Operation that can be used in a query.
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Op {
And,
Or,
}

lazy_static::lazy_static! {
static ref PRATT_PARSER: PrattParser<Rule> = {
use pest::pratt_parser::{Assoc::*, Op};
use Rule::*;
use pest::pratt_parser::{Assoc::Left, Op};
use Rule::{and, or, unary_not};

PrattParser::new()
// & and | are evaluated with the same precedence
Expand All @@ -45,17 +45,18 @@ pub mod query {

#[derive(Parser)]
#[grammar = "query.pest"]
/// QueryParser is responsible for parsing the search query.
/// `QueryParser` is responsible for parsing the search query.
/// The relevant rule is `tagsearch`.
pub struct QueryParser;

/// construct_query_ast() creates an AST from a string of symbols
/// lexed by the QueryParser and a list of tags.
pub fn construct_query_ast(pairs: Pairs<Rule>, tags: Vec<&str>) -> Expr {
/// `construct_query_ast()` creates an AST from a string of symbols
/// lexed by the `QueryParser` and a list of tags.
#[must_use]
pub fn construct_query_ast(pairs: Pairs<Rule>, tags: &Vec<&str>) -> Expr {
PRATT_PARSER
.map_primary(|primary| match primary.as_rule() {
Rule::tag => Expr::Bool(tags.contains(&primary.as_str().trim())),
Rule::expr => construct_query_ast(primary.into_inner(), tags.clone()),
Rule::expr => construct_query_ast(primary.into_inner(), tags),
rule => unreachable!("Expected tag, found {:?}", rule),
})
.map_infix(|lhs, op, rhs| {
Expand All @@ -78,8 +79,9 @@ pub mod query {
.parse(pairs)
}

/// evaluate_ast() evaluates an AST created by construct_query_ast()
/// `evaluate_ast()` evaluates an AST created by `construct_query_ast()`
/// and returns the result.
#[must_use]
pub fn evaluate_ast(ast: Expr) -> bool {
match ast {
Expr::Bool(value) => value,
Expand All @@ -98,14 +100,14 @@ pub mod query {

#[cfg(test)]
mod tests {
use crate::parsers::query::construct_query_ast;
use crate::parsers::query::evaluate_ast;
use crate::parsers::query::Expr;
use crate::parsers::query::Op;
use crate::parsers::query::QueryParser;
use crate::parsers::searchquery::construct_query_ast;
use crate::parsers::searchquery::evaluate_ast;
use crate::parsers::searchquery::Expr;
use crate::parsers::searchquery::Op;
use crate::parsers::searchquery::QueryParser;

use super::query;
use super::tagline;
use super::onfile;
use super::searchquery;

use pest::Parser;

Expand Down Expand Up @@ -151,10 +153,10 @@ mod tests {
},
];

test_cases.iter().for_each(|test_case| {
for test_case in test_cases {
println!("test_tagline_parser: \n\t{}", test_case.name);

let res = tagline::TaglineParser::parse(tagline::Rule::tagline, test_case.input);
let res = onfile::TaglineParser::parse(onfile::Rule::tagline, test_case.input);
if res.is_err() {
assert!(test_case.expected_error);
return;
Expand All @@ -163,11 +165,11 @@ mod tests {
assert!(!test_case.expected_error);

for (i, tag) in res.unwrap().enumerate() {
if tag.as_rule() == tagline::Rule::tag {
if tag.as_rule() == onfile::Rule::tag {
assert_eq!(tag.as_str().trim(), test_case.expected_tags[i]);
}
}
})
}
}

#[test]
Expand Down Expand Up @@ -221,19 +223,20 @@ mod tests {
},
];

test_cases.iter().for_each(|test_case| {
for test_case in test_cases {
println!("test_query_parser: \n\t{}", test_case.name);

let res = query::QueryParser::parse(query::Rule::tagsearch, test_case.input);
let res =
searchquery::QueryParser::parse(searchquery::Rule::tagsearch, test_case.input);
if res.is_err() {
assert!(test_case.expected_error);
return;
}

assert!(!test_case.expected_error);

assert_eq!(test_case.input, res.unwrap().as_str())
})
assert_eq!(test_case.input, res.unwrap().as_str());
}
}

#[test]
Expand Down Expand Up @@ -276,24 +279,24 @@ mod tests {
},
];

test_cases.iter().for_each(|test_case| {
for test_case in test_cases {
println!("test_construct_query_ast: \n\t{}", test_case.name);

let ast = construct_query_ast(
QueryParser::parse(query::Rule::tagsearch, test_case.input_query)
QueryParser::parse(searchquery::Rule::tagsearch, test_case.input_query)
.unwrap()
.next()
.unwrap()
.into_inner(),
test_case
&test_case
.input_tags
.iter()
.map(|tag| tag.as_str())
.map(std::string::String::as_str)
.collect(),
);

assert_eq!(test_case.expected_ast, ast);
})
}
}

#[test]
Expand Down Expand Up @@ -333,13 +336,13 @@ mod tests {
},
];

test_cases.iter().for_each(|test_case| {
for test_case in test_cases {
println!("test_evaluate_ast: \n\t{}", test_case.name);

assert_eq!(
test_case.expected_result,
evaluate_ast(test_case.input_ast.clone())
)
})
);
}
}
}
20 changes: 12 additions & 8 deletions src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,42 @@ use std::{
use pest::Parser;
use walkdir::WalkDir;

use crate::parsers::tagline::{self, TaglineParser};
use crate::parsers::onfile::{self, TaglineParser};

/// TaggedFile is a file that contains tags.
/// `TaggedFile` is a file that contains tags.
#[derive(Clone, Debug)]
pub struct TaggedFile {
pub path: PathBuf,
pub tags: Vec<String>,
}

/// get_tags_from_file() returns a list of tags found in a file.
/// `get_tags_from_file()` returns a list of tags found in a file.
/// It will return an error if a file has no parsable tags.
fn get_tags_from_file(file: &Path) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let file = fs::File::open(file)?;
let mut buffer = BufReader::new(file);
let mut tagline = String::new();
let _ = buffer.read_line(&mut tagline)?;

let parsed = TaglineParser::parse(tagline::Rule::tagline, tagline.trim())?;
let parsed = TaglineParser::parse(onfile::Rule::tagline, tagline.trim())?;

let mut tags = Vec::new();

for tag in parsed {
if tag.as_rule() == tagline::Rule::tag {
tags.push(tag.as_str().to_string())
if tag.as_rule() == onfile::Rule::tag {
tags.push(tag.as_str().to_string());
}
}

Ok(tags)
}

/// get_tags_from_files() recursively retrieves the tags of all files
/// `get_tags_from_files()` recursively retrieves the tags of all files
/// in a given directory.
///
/// # Errors
///
/// This function errors if it fails to walk the given directory.
pub fn get_tags_from_files(directory: &str) -> Result<Vec<TaggedFile>, Box<dyn std::error::Error>> {
let mut tagged_files = Vec::new();

Expand All @@ -55,7 +59,7 @@ pub fn get_tags_from_files(directory: &str) -> Result<Vec<TaggedFile>, Box<dyn s
tagged_files.push(TaggedFile {
path: entry.path().to_owned(),
tags,
})
});
}
}

Expand Down

0 comments on commit 5b788c7

Please sign in to comment.