Skip to content

Commit ba063b2

Browse files
authored
feat: fix some bugs, improve some perf, add some tests (#38)
* feat: fix some bugs, improve some perf, add some tests * fix: lint
1 parent 2403afb commit ba063b2

15 files changed

Lines changed: 493 additions & 95 deletions

File tree

.github/workflows/ci.yml

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,25 +32,10 @@ jobs:
3232

3333
steps:
3434
- name: Checkout repository
35-
uses: actions/checkout@v3
35+
uses: actions/checkout@v4
3636

3737
- name: Install Rust toolchain
38-
uses: actions-rs/toolchain@v1
39-
with:
40-
toolchain: stable
41-
target: ${{ matrix.target }}
42-
profile: minimal
43-
override: true
44-
45-
- name: Install Rust library source
46-
if: matrix.target == 'x86_64-unknown-linux-gnu'
47-
uses: actions-rs/toolchain@v1
48-
with:
49-
toolchain: stable
50-
target: ${{ matrix.target }}
51-
profile: minimal
52-
override: true
53-
components: rust-src
38+
run: rustup toolchain install stable --target ${{ matrix.target }} --profile minimal
5439

5540
- name: Build
5641
run: cargo build --verbose --target ${{ matrix.target }}
@@ -59,15 +44,13 @@ jobs:
5944
run: cargo test --verbose --target ${{ matrix.target }}
6045

6146
lint:
62-
name: Formatter
63-
64-
needs: build-test
47+
name: Lint & Format
6548

6649
runs-on: ubuntu-latest
6750

6851
steps:
6952
- name: Checkout repository
70-
uses: actions/checkout@v3
53+
uses: actions/checkout@v4
7154

7255
- name: Install Rust
7356
run: |
@@ -79,5 +62,5 @@ jobs:
7962
- name: Check formatting
8063
run: cargo fmt --all -- --check
8164

82-
- name: Check code for possible improvements
65+
- name: Run clippy
8366
run: cargo clippy -- -D warnings

README.md

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -28,52 +28,48 @@ rust_search = "2.0.0"
2828
```rust
2929
use rust_search::SearchBuilder;
3030

31-
fn main(){
32-
let search: Vec<String> = SearchBuilder::default()
33-
.location("~/path/to/directory")
34-
.search_input("what to search")
35-
.more_locations(vec!["/anotherPath/to/search", "/keepAddingIfYouWant/"])
36-
.limit(1000) // results to return
37-
.ext("extension")
38-
.strict()
39-
.depth(1)
40-
.ignore_case()
41-
.hidden()
42-
.build()
43-
.collect();
44-
45-
for path in search {
46-
println!("{}", path);
47-
}
31+
let search: Vec<String> = SearchBuilder::default()
32+
.location("~/path/to/directory")
33+
.search_input("what to search")
34+
.more_locations(vec!["/anotherPath/to/search", "/keepAddingIfYouWant/"])
35+
.limit(1000) // results to return
36+
.ext("extension")
37+
.strict()
38+
.depth(1)
39+
.ignore_case()
40+
.hidden()
41+
.build()
42+
.collect();
43+
44+
for path in search {
45+
println!("{}", path);
4846
}
4947
```
5048

5149
- Sort the output by similarity with the input
5250

5351
```rust
54-
use rust_search::{SearchBuilder, similarity_sort};
55-
fn main() {
56-
let search_input = "fly";
57-
let mut search: Vec<String> = SearchBuilder::default()
58-
.location("~/Desktop/")
59-
.search_input(search_input)
60-
.depth(1)
61-
.ignore_case()
62-
.build()
63-
.collect();
64-
65-
similarity_sort(&mut search, &search_input);
66-
for path in search {
67-
println!("{:?}", path);
68-
}
69-
}
70-
52+
use rust_search::{SearchBuilder, similarity_sort};
53+
54+
let search_input = "fly";
55+
let mut search: Vec<String> = SearchBuilder::default()
56+
.location("~/Desktop/")
57+
.search_input(search_input)
58+
.depth(1)
59+
.ignore_case()
60+
.build()
61+
.collect();
62+
63+
similarity_sort(&mut search, &search_input);
64+
for path in search {
65+
println!("{:?}", path);
66+
}
7167
```
7268
> search **without** similarity sort
73-
`["afly.txt", "bfly.txt", "flyer.txt", "fly.txt"]`
69+
> `["afly.txt", "bfly.txt", "flyer.txt", "fly.txt"]`
7470
7571
> search **with** similarity sort
76-
`["fly.txt", "flyer.txt", "afly.txt", "bfly.txt",]`
72+
> `["fly.txt", "flyer.txt", "afly.txt", "bfly.txt",]`
7773
7874
- To get all the files with a specific extension in a directory, use:
7975

@@ -98,7 +94,7 @@ let files: Vec<String> = SearchBuilder::default()
9894
.build()
9995
.collect();
10096
```
101-
To filter files by date_created, date_modified, file_size and/or custom_filter, use:
97+
To filter files by `date_created`, `date_modified`, `file_size` and/or `custom_filter`, use:
10298

10399
```rust
104100
use rust_search::{FileSize, FilterExt, SearchBuilder};
@@ -121,7 +117,7 @@ let search: Vec<String> = SearchBuilder::default()
121117

122118
## ⚙️ Benchmarks
123119

124-
The difference in sample size is due to the fact that fd and glob are different tools and have different use cases. fd is a command line tool that searches for files and directories. glob is a library that can be used to search for files and directories. The benchmark is done on a MacBook Air M2, 16 GB Unified memory.
120+
The difference in sample size is due to the fact that fd and glob are different tools and have different use cases. fd is a command line tool that searches for files and directories. glob is a library that can be used to search for files and directories. The benchmark is done on a `MacBook` Air M2, 16 GB Unified memory.
125121

126122
Benchmarks are done using [hyperfine](https://github.com/sharkdp/hyperfine),
127123
Benchmarks files are available in the [benchmarks](https://drive.google.com/drive/folders/1ug6ojNixS5jAe6Lh6M0o2d3tku73zQ9w?usp=sharing) drive folder.

src/builder.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,10 @@ impl SearchBuilder {
9696
pub fn ext(mut self, ext: impl Into<String>) -> Self {
9797
let ext: String = ext.into();
9898
// Remove the dot if it's there.
99-
self.file_ext = Some(ext.strip_prefix('.').map_or(ext.clone(), str::to_owned));
99+
self.file_ext = Some(
100+
ext.strip_prefix('.')
101+
.map_or_else(|| ext.clone(), str::to_owned),
102+
);
100103
self
101104
}
102105

@@ -201,7 +204,7 @@ impl SearchBuilder {
201204
/// use rust_search::SearchBuilder;
202205
///
203206
/// let search: Vec<String> = SearchBuilder::default()
204-
/// .with_hidden()
207+
/// .hidden()
205208
/// .build()
206209
/// .collect();
207210
/// ```

src/filter.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,11 @@ fn convert(b: f64, pow: u32) -> u64 {
5757
(b * 1024_u64.pow(pow) as f64) as u64
5858
}
5959

60-
#[allow(clippy::from_over_into)]
61-
impl Into<u64> for FileSize {
62-
fn into(self) -> u64 {
60+
impl From<FileSize> for u64 {
61+
fn from(size: FileSize) -> Self {
6362
use self::FileSize::{Byte, Gigabyte, Kilobyte, Megabyte, Terabyte};
6463

65-
match self {
64+
match size {
6665
Byte(b) => b,
6766
Kilobyte(b) => convert(b, 1),
6867
Megabyte(b) => convert(b, 2),
@@ -94,7 +93,12 @@ pub trait FilterExt {
9493
fn file_size_greater(self, size: FileSize) -> Self;
9594
/// custom filter that exposes the [`DirEntry`] directly
9695
/// ```rust
97-
/// builder.custom_filter(|dir| dir.metadata().unwrap().is_file())
96+
/// use rust_search::{SearchBuilder, FilterExt};
97+
///
98+
/// let search: Vec<String> = SearchBuilder::default()
99+
/// .custom_filter(|dir| dir.metadata().unwrap().is_file())
100+
/// .build()
101+
/// .collect();
98102
/// ```
99103
fn custom_filter(self, f: FilterFn) -> Self;
100104
}

src/search.rs

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
use std::{
22
cmp,
33
path::Path,
4-
sync::mpsc::{self, Sender},
4+
sync::{
5+
atomic::{AtomicUsize, Ordering},
6+
mpsc::{self, Sender},
7+
Arc,
8+
},
59
};
610

711
use crate::{filter::FilterType, utils, SearchBuilder};
812
use ignore::{WalkBuilder, WalkState};
9-
use regex::Regex;
1013

1114
/// A struct that holds the receiver for the search results
1215
///
@@ -17,9 +20,13 @@ use regex::Regex;
1720
/// ## Iterate on the results
1821
///
1922
/// ```
20-
/// use rust_search::Search;
23+
/// use rust_search::SearchBuilder;
2124
///
22-
/// let search = Search::new("src", None, Some(".rs"), Some(1));
25+
/// let search = SearchBuilder::default()
26+
/// .location("src")
27+
/// .ext("rs")
28+
/// .depth(1)
29+
/// .build();
2330
///
2431
/// for path in search {
2532
/// println!("{:?}", path);
@@ -29,11 +36,14 @@ use regex::Regex;
2936
/// ## Collect results into a vector
3037
///
3138
/// ```
32-
/// use rust_search::Search;
39+
/// use rust_search::SearchBuilder;
3340
///
34-
/// let search = Search::new("src", None, Some(".rs"), Some(1));
35-
///
36-
/// let paths_vec: Vec<String> = search.collect();
41+
/// let paths_vec: Vec<String> = SearchBuilder::default()
42+
/// .location("src")
43+
/// .ext("rs")
44+
/// .depth(1)
45+
/// .build()
46+
/// .collect();
3747
/// ```
3848
pub struct Search {
3949
rx: Box<dyn Iterator<Item = String>>,
@@ -94,10 +104,13 @@ impl Search {
94104
}
95105

96106
let (tx, rx) = mpsc::channel::<String>();
107+
let reg_exp = Arc::new(regex_search_input);
108+
let counter = Arc::new(AtomicUsize::new(0));
109+
97110
walker.build_parallel().run(|| {
98111
let tx: Sender<String> = tx.clone();
99-
let reg_exp: Regex = regex_search_input.clone();
100-
let mut counter = 0;
112+
let reg_exp = Arc::clone(&reg_exp);
113+
let counter = Arc::clone(&counter);
101114

102115
Box::new(move |path_entry| {
103116
if let Ok(entry) = path_entry {
@@ -106,17 +119,13 @@ impl Search {
106119
// Lossy means that if the file name is not valid UTF-8
107120
// it will be replaced with �.
108121
// Will return the file name with extension.
109-
let file_name = file_name.to_string_lossy().to_string();
122+
let file_name = file_name.to_string_lossy();
110123
if reg_exp.is_match(&file_name) {
111-
// Continue searching if the send was successful
112-
// and there is no limit or the limit has not been reached
113-
if tx.send(path.display().to_string()).is_ok()
114-
&& (limit.is_none() || counter < limit.unwrap())
124+
if limit.is_none_or(|l| counter.fetch_add(1, Ordering::Relaxed) < l)
125+
&& tx.send(path.display().to_string()).is_ok()
115126
{
116-
counter += 1;
117127
return WalkState::Continue;
118128
}
119-
120129
return WalkState::Quit;
121130
}
122131
}

0 commit comments

Comments
 (0)