From 2403afbf1fc117af8e90462c3e8d5b0c52882e82 Mon Sep 17 00:00:00 2001 From: Xithrius <15021300+Xithrius@users.noreply.github.com> Date: Mon, 2 Jan 2023 20:55:15 -0800 Subject: [PATCH] chore: add clippy linting and CI build/test (#30) --- .github/workflows/ci.yml | 83 ++++++++++++++++++++++++++++++++++++++++ src/builder.rs | 12 +++--- src/filter.rs | 37 +++++++++--------- src/lib.rs | 12 +++++- src/search.rs | 6 +-- src/utils.rs | 54 ++++++++++++++------------ 6 files changed, 151 insertions(+), 53 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ea4cbb3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,83 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + +env: + CARGO_TERM_COLOR: always + +jobs: + build-test: + strategy: + fail-fast: false + + matrix: + include: + - os: windows-latest + target: x86_64-pc-windows-msvc + + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + + - os: macos-latest + target: x86_64-apple-darwin + + name: Build & Test (${{ matrix.target }}) + runs-on: ${{ matrix.os }} + + env: + RA_TARGET: ${{ matrix.target }} + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: ${{ matrix.target }} + profile: minimal + override: true + + - name: Install Rust library source + if: matrix.target == 'x86_64-unknown-linux-gnu' + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: ${{ matrix.target }} + profile: minimal + override: true + components: rust-src + + - name: Build + run: cargo build --verbose --target ${{ matrix.target }} + + - name: Run tests + run: cargo test --verbose --target ${{ matrix.target }} + + lint: + name: Formatter + + needs: build-test + + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Rust + run: | + rustup update stable + rustup default stable + rustup component add rustfmt + rustup component add clippy + + - name: Check formatting + run: cargo fmt --all -- --check + + - name: Check code for possible improvements + run: cargo clippy -- -D warnings diff --git a/src/builder.rs b/src/builder.rs index ecdfb1c..6351a40 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -96,7 +96,7 @@ impl SearchBuilder { pub fn ext(mut self, ext: impl Into) -> Self { let ext: String = ext.into(); // Remove the dot if it's there. - self.file_ext = Some(ext.strip_prefix('.').map(str::to_owned).unwrap_or(ext)); + self.file_ext = Some(ext.strip_prefix('.').map_or(ext.clone(), str::to_owned)); self } @@ -137,7 +137,7 @@ impl SearchBuilder { /// .build() /// .collect(); /// ``` - pub fn depth(mut self, depth: usize) -> Self { + pub const fn depth(mut self, depth: usize) -> Self { self.depth = Some(depth); self } @@ -154,7 +154,7 @@ impl SearchBuilder { /// .build() /// .collect(); /// ``` - pub fn limit(mut self, limit: usize) -> Self { + pub const fn limit(mut self, limit: usize) -> Self { self.limit = Some(limit); self } @@ -172,7 +172,7 @@ impl SearchBuilder { /// .build() /// .collect(); /// ``` - pub fn strict(mut self) -> Self { + pub const fn strict(mut self) -> Self { self.strict = true; self } @@ -190,7 +190,7 @@ impl SearchBuilder { /// .build() /// .collect(); /// ``` - pub fn ignore_case(mut self) -> Self { + pub const fn ignore_case(mut self) -> Self { self.ignore_case = true; self } @@ -205,7 +205,7 @@ impl SearchBuilder { /// .build() /// .collect(); /// ``` - pub fn hidden(mut self) -> Self { + pub const fn hidden(mut self) -> Self { self.hidden = true; self } diff --git a/src/filter.rs b/src/filter.rs index 1064643..2f8c308 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -17,27 +17,27 @@ impl FilterType { pub fn apply(&self, dir: &DirEntry) -> bool { if let Ok(m) = dir.metadata() { match self { - FilterType::Created(cmp, time) => { + Self::Created(cmp, time) => { if let Ok(created) = m.created() { return created.cmp(time) == *cmp; } } - FilterType::Modified(cmp, time) => { + Self::Modified(cmp, time) => { if let Ok(modified) = m.modified() { return modified.cmp(time) == *cmp; } } - FilterType::FileSize(cmp, size_in_bytes) => { + Self::FileSize(cmp, size_in_bytes) => { return m.len().cmp(size_in_bytes) == *cmp; } - FilterType::Custom(f) => return f(dir), + Self::Custom(f) => return f(dir), } } false } } -/// enum to easily convert between byte_sizes +/// enum to easily convert between `byte_sizes` #[derive(Debug, Clone)] pub enum FileSize { /// size in bytes @@ -60,7 +60,8 @@ fn convert(b: f64, pow: u32) -> u64 { #[allow(clippy::from_over_into)] impl Into for FileSize { fn into(self) -> u64 { - use self::FileSize::*; + use self::FileSize::{Byte, Gigabyte, Kilobyte, Megabyte, Terabyte}; + match self { Byte(b) => b, Kilobyte(b) => convert(b, 1), @@ -73,17 +74,17 @@ impl Into for FileSize { /// import this trait to filter files pub trait FilterExt { - /// files created before `t`: [SystemTime] + /// files created before `t`: [`SystemTime`] fn created_before(self, t: SystemTime) -> Self; - /// files created at `t`: [SystemTime] + /// files created at `t`: [`SystemTime`] fn created_at(self, t: SystemTime) -> Self; - /// files created after `t`: [SystemTime] + /// files created after `t`: [`SystemTime`] fn created_after(self, t: SystemTime) -> Self; - /// files created before `t`: [SystemTime] + /// files created before `t`: [`SystemTime`] fn modified_before(self, t: SystemTime) -> Self; - /// files modified at `t`: [SystemTime] + /// files modified at `t`: [`SystemTime`] fn modified_at(self, t: SystemTime) -> Self; - /// files modified after `t`: [SystemTime] + /// files modified after `t`: [`SystemTime`] fn modified_after(self, t: SystemTime) -> Self; /// files smaller than `size_in_bytes`: [usize] fn file_size_smaller(self, size: FileSize) -> Self; @@ -91,15 +92,15 @@ pub trait FilterExt { fn file_size_equal(self, size: FileSize) -> Self; /// files greater than `size_in_bytes`: [usize] fn file_size_greater(self, size: FileSize) -> Self; - /// custom filter that exposes the [DirEntry] directly + /// custom filter that exposes the [`DirEntry`] directly /// ```rust /// builder.custom_filter(|dir| dir.metadata().unwrap().is_file()) /// ``` fn custom_filter(self, f: FilterFn) -> Self; } -use FilterType::*; -use Ordering::*; +use FilterType::{Created, Custom, FileSize as FilterFileSize, Modified}; +use Ordering::{Equal, Greater, Less}; impl FilterExt for SearchBuilder { fn created_before(self, t: SystemTime) -> Self { self.filter(Created(Less, t)) @@ -126,15 +127,15 @@ impl FilterExt for SearchBuilder { } fn file_size_smaller(self, size: FileSize) -> Self { - self.filter(FileSize(Less, size.into())) + self.filter(FilterFileSize(Less, size.into())) } fn file_size_equal(self, size: FileSize) -> Self { - self.filter(FileSize(Equal, size.into())) + self.filter(FilterFileSize(Equal, size.into())) } fn file_size_greater(self, size: FileSize) -> Self { - self.filter(FileSize(Greater, size.into())) + self.filter(FilterFileSize(Greater, size.into())) } fn custom_filter(self, f: FilterFn) -> Self { self.filter(Custom(f)) diff --git a/src/lib.rs b/src/lib.rs index 9c4572b..3f29197 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,13 @@ +#![warn(clippy::nursery, clippy::pedantic)] +#![allow( + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + clippy::cast_precision_loss, + clippy::module_name_repetitions, + clippy::unused_self, + clippy::return_self_not_must_use, + clippy::must_use_candidate +)] #![warn(missing_docs)] // Use the readme as the crate documentation #![doc = include_str!("../README.md")] @@ -13,4 +23,4 @@ pub use filter::{FileSize, FilterExt, FilterFn}; // export this in order to use it with custom filter functions pub use ignore::DirEntry; pub use search::Search; -pub use utils::similarity_sort; \ No newline at end of file +pub use utils::similarity_sort; diff --git a/src/search.rs b/src/search.rs index dea2bd4..89a235c 100644 --- a/src/search.rs +++ b/src/search.rs @@ -58,7 +58,7 @@ impl Search { /// * `strict` - Whether to search for the exact word or not /// * `ignore_case` - Whether to ignore case or not /// * `hidden` - Whether to search hidden files or not - /// * `filters` - Vector of filters to search by DirEntry data + /// * `filters` - Vector of filters to search by `DirEntry` data #[allow(clippy::too_many_arguments)] pub(crate) fn new( search_location: impl AsRef, @@ -115,9 +115,9 @@ impl Search { { counter += 1; return WalkState::Continue; - } else { - return WalkState::Quit; } + + return WalkState::Quit; } } } diff --git a/src/utils.rs b/src/utils.rs index c7ef6c1..3d77a5d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -2,7 +2,9 @@ use regex::Regex; use std::path::{Path, PathBuf}; use strsim::jaro_winkler; -pub(crate) fn build_regex_search_input( +const FUZZY_SEARCH: &str = r".*"; + +pub fn build_regex_search_input( search_input: Option<&str>, file_ext: Option<&str>, strict: bool, @@ -10,13 +12,13 @@ pub(crate) fn build_regex_search_input( ) -> Regex { let file_type = file_ext.unwrap_or("*"); let search_input = search_input.unwrap_or(r"\w+"); - const FUZZY_SEARCH: &str = r".*"; - let mut formatted_search_input; - if strict { - formatted_search_input = format!(r#"{}\.{}$"#, search_input, file_type); + + let mut formatted_search_input = if strict { + format!(r#"{search_input}\.{file_type}$"#) } else { - formatted_search_input = format!(r#"{}{}\.{}$"#, search_input, FUZZY_SEARCH, file_type); - } + format!(r#"{search_input}{FUZZY_SEARCH}\.{file_type}$"#) + }; + if ignore_case { formatted_search_input = set_case_insensitive(&formatted_search_input); } @@ -30,7 +32,7 @@ fn set_case_insensitive(formatted_search_input: &str) -> String { /// Replace the tilde with the home directory, if it exists /// ### Arguments /// * `path` - The path to replace the tilde with the home directory -pub(crate) fn replace_tilde_with_home_dir(path: impl AsRef) -> PathBuf { +pub fn replace_tilde_with_home_dir(path: impl AsRef) -> PathBuf { let path = path.as_ref(); if path.starts_with("~") { if let Some(home_dir) = dirs::home_dir() { @@ -44,7 +46,7 @@ pub(crate) fn replace_tilde_with_home_dir(path: impl AsRef) -> PathBuf { fn file_name_from_path(path: &str) -> String { let path = Path::new(path); let file_name = path.file_name().unwrap().to_str().unwrap(); - return file_name.to_string(); + file_name.to_string() } /// This function can be used to sort the given vector on basis of similarity between the input & the vector @@ -54,29 +56,31 @@ fn file_name_from_path(path: &str) -> String { /// ### Examples /// ```rust /// use rust_search::{SearchBuilder, similarity_sort}; -/// fn main() { -/// let search_input = "fly"; -/// let mut search: Vec = SearchBuilder::default() -/// .location("~/Desktop/") -/// .search_input(search_input) -/// .depth(1) -/// .ignore_case() -/// .build() -/// .collect(); +/// +/// let search_input = "fly"; +/// let mut search: Vec = SearchBuilder::default() +/// .location("~/Desktop/") +/// .search_input(search_input) +/// .depth(1) +/// .ignore_case() +/// .build() +/// .collect(); -/// similarity_sort(&mut search, &search_input); -/// for path in search { -/// println!("{:?}", path); -/// } +/// similarity_sort(&mut search, &search_input); +/// for path in search { +/// println!("{:?}", path); /// } /// ``` -/// +/// /// search **without** similarity sort /// `["afly.txt", "bfly.txt", "flyer.txt", "fly.txt"]` -/// +/// /// search **with** similarity sort /// `["fly.txt", "flyer.txt", "afly.txt", "bfly.txt",]` -pub fn similarity_sort(vector: &mut Vec, input: &str) { +/// +/// ### Panics +/// Will panic if `partial_cmp` is None +pub fn similarity_sort(vector: &mut [String], input: &str) { vector.sort_by(|a, b| { let input = input.to_lowercase(); let a = file_name_from_path(a).to_lowercase();