A multi-threaded, memory-mapped grep clone written in Rust. A multi-threaded, memory-mapped grep clone written in Rust. Demonstrates fearless concurrency, zero-copy I/O, custom error types, and clap-based CLI design.
- Memory-mapped I/O (
memmap2): files are mapped rather than read into a heap buffer, so a multi-gigabyte file costs a page-table entry to open, not a multi-gigabyte allocation. Search runs directly against the mapped bytes. - Work-stealing concurrency (
rayon+crossbeam-channel): a rayon thread pool searches files in parallel across however many threads you give it; each worker hands its results to a single printer thread over an unbounded crossbeam channel, so output never interleaves and no lock is held around stdout. - Structured error handling (
thiserrorfor library-style errors inerror.rs,anyhowwith context at the application boundary inmain.rs). - CLI parsing (
clap, derive API) with grep-familiar flags.
cargo build --release
The binary is produced at target/release/tgrep.
tgrep [OPTIONS] <PATTERN> [PATHS]...
Common flags:
| Flag | Meaning |
|---|---|
-i, --ignore-case |
case insensitive search |
-F, --fixed-strings |
treat pattern as a literal string, not a regex |
-w, --word-regexp |
match whole words only |
-c, --count |
print per-file match counts instead of lines |
-A/-B/-C <N> |
lines of context after / before / both |
-t, --type <ext> |
restrict to a file extension, repeatable |
--hidden |
include hidden files and skip the default ignore list |
--binary |
also search files that look like binaries |
-j, --threads <N> |
worker thread count, defaults to logical CPU count |
--stats |
print a files searched / matched / elapsed summary to stderr |
By default, tgrep skips hidden files and the usual noise directories
(.git, target, node_modules, .venv, __pycache__, .idea,
.vscode), and skips files that look binary (a null byte in the first 8 KB).
Both are overridable with --hidden and --binary.
Exit codes follow grep's convention: 0 if something matched, 1 if
nothing matched, 2 on an error (bad pattern, unreadable path, etc).
tgrep "TODO" src
tgrep -i -c "error" . -t rs -t toml
tgrep -F -w "mmap" src --stats
tgrep -C 2 "fn main" src/main.rs
src/
main.rs wires the CLI, thread pool, and printer together
cli.rs clap argument definitions
walker.rs directory traversal and file filtering
searcher.rs per-file memory-mapped search
output.rs the printer thread and colored formatting
error.rs thiserror error types for I/O and mmap failures
- Match byte-offsets are computed against the raw file bytes and then
rendered against a lossily-decoded
String. For files that are not valid UTF-8, the replacement character (\u{FFFD}) is a different byte width than the original byte, so a highlighted range can be off by a character or two on those lines. Valid UTF-8 and ASCII files, which is the large majority of source code and logs, are unaffected. - No
.gitignore-aware filtering (only the fixed default-ignore list above). Adding that would mean pulling in theignorecrate, which is ripgrep's own crate for exactly this.