💾 Archived View for dmerej.info › en › blog › 0088-ruplacer.gmi captured on 2023-01-29 at 15:53:25. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2022-07-16)
-=-=-=-=-=-=-
2018, Oct 14 - Dimitri Merejkowsky License: CC By 4.0
Today I'd like to talk about a command-line tool I've been working on.
It's called ruplacer[1] and as the name suggest, it's *rually* cool and written in Rust.
1: https://github.com/dmerejkowsky/ruplacer
Basically, it finds and replaces text in source files. Here's a screenshot of ruplacer in action:
Some nice features:
# Replaces dates looking like DD/MM/YYYY to YYYY-MM-DD $ ruplacer '(\d{2})/(\d{2})/(\d{4})' '$3-$1-$2'
$ ruplacer --subvert foo_bar spam_eggs Patching src/foo.txt -- foo_bar, FooBar, and FOO_BAR! ++ spam_eggs, SpamEggs, and SPAM_EGGS!
Here's how it works:
First, we build a structopt[2] struct for the command-line arguments parsing. Depending on the presence of the `--subvert` or `--no-rexeg` flags, we build a *Query*, which can be of several types: `Substring`, `Regex` or `Subvert`.
2: https://crates.io/crates/structopt
Then we leverage the ignore[3] crate to walk through every file in the source directory while skipping files listed in `.gitignore`. By the way, the ignore crates comes directly from ripgrep[4], an awesome alternative to `grep` also written in Rust.
3: https://crates.io/crates/ignore
4: https://github.com/BurntSushi/ripgrep
Along the way, we build a *FilePatcher* from the source file and the query. The FilePatcher goes through every line of the file and then sends it along with the query to a *LinePatcher*.
The LinePatcher runs the code corresponding to the query type and returns a new string, using the Inflector[5] crate to perform case string conversions if required.
5: https://crates.io/crates/Inflector
Finally, if the string has changed, the FilePatcher builds a *Replacement* struct and pretty-prints it to the user. While doing so, it also keeps a record of the modified contents of the file. Finally, if not in dry-run mode, it overwrites the file with the new contents.
And that's pretty much it :)
The idea of ruplacer started almost a decade ago when a colleague of mine showed me a shell function called `replacer` (thanks, Cédric!) It was basically a mixture of calls to `find`, `sed` and `diff`. You can still find it online[6].
Because I wanted better cross-platform support, a dry-run mode and a colorful output, I rewrote it in Python[7] a few years ago. Along the way, the features, command line syntax and the style of the output changed quite a lot, but I've been using it regularly for all this time.
7: https://github.com/dmerejkowsky/replacer
Finally, after hearing about ripgrep and fd[8], I decided to give Rust a go, and that's how ruplacer, the third incarnation of this tool, was born. This makes me confident it's good enough for *you* to try.
8: https://github.com/sharkdp/fd
If you have `cargo` installed, you can get ruplacer by running `cargo install ruplacer`. Otherwise, you will find the source code[9] and pre-compiled binaries[10] on GitHub.
9: https://github.com/dmerejkowsky/ruplacer/tree/master/src
10: https://github.com/dmerejkowsky/ruplacer/releases
Cheers!
----