💾 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

View Raw

More Information

⬅️ Previous capture (2022-07-16)

-=-=-=-=-=-=-

2018, Oct 14 - Dimitri Merejkowsky
License: CC By 4.0

Introduction

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:

ruplacer screenshot [IMG]

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!

How it works

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 :)

Why I'm sharing this

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].

6: https://github.com/cgestes/ctafconf/blob/78b92a60bc185b73f95418e3e913e33aae8799f6/bin/replacer#L75

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!

----

Back to Index

Contact me