# superwhich
 
`superwhich` is a cross-platform CLI tool and library that uses [Jaro-Winkler distance](https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance) to calculate the similarity between the strings, printing only the executables in the `PATH` environment variable that match the search pattern with the provided threshold.
## MSRV
| 3.x.y | 2024 | 1.85 |
| 2.x.y | 2021 | 1.80 |
| 1.x.y | 2021 | N/A |
## Installation
### CLI
- From [crates.io](https://crates.io/crates/superwhich): `cargo install superwhich --features cli`
- From [GitHub](https://github.com/DarkCeptor44/superwhich): `cargo install --git https://github.com/DarkCeptor44/superwhich --features cli`
- Manually (after cloning the repo locally): `cargo install --path . --features cli`
- From [releases](https://github.com/DarkCeptor44/superwhich/releases/latest).
### Library
```bash
cargo add superwhich
```
Or you can add this to your `Cargo.toml` file:
```toml
[dependencies]
superwhich = "3.0.0"
```
## Usage
### CLI
```sh
$ swhich -h
Cross-platform smart which alternative
Usage: swhich [OPTIONS] <PATTERN>
Arguments:
<PATTERN> The search pattern
Options:
-c, --color <COLOR> Color of the highlighted text (off or set `NO_COLOR` env var to disable) [default: blue]
-t, --threads <THREADS> Number of threads to use (0 for auto) [default: 75% of CPU cores]
-T, --threshold <THRESHOLD> String similarity threshold (0.0 to 1.0) [default: 0.7]
-h, --help Print help
-V, --version Print version
```
### Library
```rust
use colored::Color;
use rayon::iter::{ParallelBridge, ParallelIterator};
use std::{
env::{split_paths, var_os},
path::{Path, PathBuf},
sync::Arc,
};
use superwhich::{
find_files, par_find_files, SearchCtx,
crossbeam::channel::unbounded,
};
// construct the context needed for the search
let ctx = Arc::new(SearchCtx::new("GURE".to_string()).threshold(0.7).color(Color::Red));
let (tx, rx) = unbounded(); // channel to send found paths in
// run the search directly with a singular path
let path = Path::new("path/to/search");
par_find_files(&path, &ctx, &tx);
// or run the search inside a parallel iterator
});
// DO NOT run the parallel function inside a parallel iterator, it actually slows down the search considerably
// print the found paths (they're String)
while let Ok(path) = rx.try_recv() {
println!("{}", path);
}
```
## Tests
You can run the tests with `cargo test`.
## Benchmarks
### Library
```text
Timer precision: 100 ns
str fastest │ slowest │ median │ mean │ samples │ iters
├─ fast lowercase 2.554 ms │ 3.497 ms │ 2.666 ms │ 2.74 ms │ 100 │ 100
│ 39.14 Mitem/s │ 28.59 Mitem/s │ 37.5 Mitem/s │ 36.49 Mitem/s │ │
│ max alloc: │ │ │ │ │
│ 1 │ 1 │ 1 │ 1 │ │
│ 2.4 MB │ 2.4 MB │ 2.4 MB │ 2.4 MB │ │
│ alloc: │ │ │ │ │
│ 1 │ 1 │ 1 │ 1 │ │
│ 2.4 MB │ 2.4 MB │ 2.4 MB │ 2.4 MB │ │
╰─ std lowercase 4.877 ms │ 7.723 ms │ 5.288 ms │ 5.456 ms │ 100 │ 100
20.5 Mitem/s │ 12.94 Mitem/s │ 18.91 Mitem/s │ 18.32 Mitem/s │ │
max alloc: │ │ │ │ │
100001 │ 100001 │ 100001 │ 100001 │ │
4.9 MB │ 4.9 MB │ 4.9 MB │ 4.9 MB │ │
alloc: │ │ │ │ │
100001 │ 100001 │ 100001 │ 100001 │ │
4.9 MB │ 4.9 MB │ 4.9 MB │ 4.9 MB │ │
```
```text
Timer precision: 100 ns
swhich fastest │ slowest │ median │ mean │ samples │ iters
├─ find_files (1000 files) 505.7 µs │ 1.263 ms │ 514.7 µs │ 548.2 µs │ 100 │ 100
│ 1.977 Mitem/s │ 791.4 Kitem/s │ 1.942 Mitem/s │ 1.824 Mitem/s │ │
│ max alloc: │ │ │ │ │
│ 4 │ 4 │ 4 │ 4 │ │
│ 289 B │ 289 B │ 289 B │ 289 B │ │
│ alloc: │ │ │ │ │
│ 1004 │ 1004 │ 1004 │ 1004 │ │
│ 12.13 KB │ 12.13 KB │ 12.13 KB │ 12.13 KB │ │
│ dealloc: │ │ │ │ │
│ 1004 │ 1004 │ 1004 │ 1004 │ │
│ 12.18 KB │ 12.18 KB │ 12.18 KB │ 12.18 KB │ │
│ grow: │ │ │ │ │
│ 1 │ 1 │ 1 │ 1 │ │
│ 47 B │ 47 B │ 47 B │ 47 B │ │
╰─ par_find_files (1000 files) 1.007 ms │ 2.26 ms │ 1.266 ms │ 1.321 ms │ 100 │ 100
992.1 Kitem/s │ 442.4 Kitem/s │ 789.6 Kitem/s │ 756.6 Kitem/s │ │
max alloc: │ │ │ │ │
4 │ 4 │ 4 │ 5.49 │ │
289 B │ 289 B │ 289 B │ 852 B │ │
alloc: │ │ │ │ │
5 │ 5 │ 5 │ 6.56 │ │
258 B │ 258 B │ 258 B │ 828.3 B │ │
dealloc: │ │ │ │ │
3 │ 3 │ 3 │ 3.06 │ │
210 B │ 210 B │ 210 B │ 221.2 B │ │
grow: │ │ │ │ │
1 │ 1 │ 1 │ 1 │ │
47 B │ 47 B │ 47 B │ 47 B │ │
```
### CLI
The CLI was benchmarked using [Hyperfine](https://github.com/sharkdp/hyperfine).
- **Windows:** AMD64, 32GB RAM, Ryzen 7 3800X, Windows 11
| `swhich grep -T0.8` (v3) | 19.2 ± 1.8 | 15.5 | 25.1 | 1.00 |
| `where grep` | 65.8 ± 3.7 | 61.1 | 81.9 | 3.43 ± 0.37 |
| `swhich pnpm` (v2) | 221.8 ± 3.5 | 215.7 | 229.2 | 11.55 |
- **Linux:** ARM64, 1GB RAM, Orange Pi Zero2, Debian 12
| `which grep` | 3.3 ± 0.7 | 2.8 | 7.2 | 1.00 |
| `swhich gurep -T0.9` (v3) | 8.5 ± 0.2 | 8.1 | 9.4 | 2.55 ± 0.54 |
| `swhich lookfor` (v2) | 17.6 ± 0.3 | 17.0 | 19.8 | 5.33 ± 1.13 |
## License
This project is licensed under the terms of the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.html).