superwhich 3.0.0

Cross-platform smart which alternative
Documentation
# superwhich


![usage-windows](assets/usage-windows.png) ![usage-linux](assets/usage-linux.png)

`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


| Version | Edition | 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
split_paths(&var_os("PATH").expect("PATH is not set")).par_bridge().for_each(|path| {
    find_files(&path, &ctx, &tx);
});

// 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

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
| --- | --- | --- | --- | --- |
| `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

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
| --- | --- | --- | --- | --- |
| `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).