petname 3.2.0

Generate human readable random names. Usable as a library and from the command-line.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# rust-petname

Generate human readable random names.

> **🚨 Upgrading?** <br>
> Check the notes on switching [**from 2.x to 3.x**]#upgrading-from-2x and
> [**from 1.x to 2.x**]#upgrading-from-1x.

[Petnames][petname-intro] are useful when you need to name a large number of
resources – like servers, services, perhaps bicycles for hire – and you want
those names to be easy to recall and communicate unambiguously. For example,
over a telephone compare saying "please restart remarkably-striking-cricket"
with "please restart s01O97i4": the former is easier to say and less likely to
be misunderstood. Avoiding sequential names adds confidence too: petnames have a
greater lexical distance between them, so errors in transcription can be more
readily detected.

This crate is both a command-line tool and a [Rust][rust-lang] library. Dustin
Kirkland's [petname][] project is the inspiration for this project. The word
lists and the basic command-line UX here are taken from there. Check it out!
Dustin maintains packages for [Python][petname-py], and [Golang][petname-go]
too.

Notable features:

- Choose from 3 built-in word lists, or provide your own.
- `english!` (aliased as `petnames!`), `turkish!`, and `luxembourgish!` macros
  (the latter two with the `lang-turkish` and `lang-luxembourgish` features) to
  statically embed word lists at compile-time.
- Alliterative names, like _viable-vulture_, _proper-pony_, ...
- Build names with 1-255 components (adjectives, adverbs, nouns).
- Name components can be unseparated, or joined by any character or string.
- Generate 1..n names, or stream names continuously.
- **`no_std` support** (see [later section]#features--no_std-support).
- Compile without built-in dictionaries to reduce library/binary size.

[rust-lang]: https://www.rust-lang.org/
[petname-intro]: https://blog.dustinkirkland.com/2015/01/introducing-petname-libraries-for.html
[petname]: https://github.com/dustinkirkland/petname
[petname-py]: https://pypi.org/project/petname/
[petname-go]: https://github.com/dustinkirkland/golang-petname

## Command-line utility

If you have [installed Cargo][install-cargo], you can install rust-petname with
`cargo install petname`. This puts a `petname` binary in `~/.cargo/bin`, which
the Cargo installation process will probably have added to your `PATH`.

> **🍺 [Homebrew][homebrew]** – You can instead install it from the
> [`allenap/utils`][allenap-utils] tap. The formula is named `rust-petname` and
> provides the same `petname` command:
>
> ```shellsession
> $ brew install allenap/utils/rust-petname
> ```

[homebrew]: https://brew.sh/
[allenap-utils]: https://github.com/allenap/homebrew-utils

The `petname` binary from rust-petname is _mostly_ drop-in compatible with the
original `petname`. It has more options and it's stricter when validating
arguments, but for most uses it should behave the same[^differences].

[^differences]:
    When using the `--dir` option, Dustin Kirkland's _petname_ looks for a
    file named `names.txt` whereas this looks for `nouns.txt` first before
    checking for `names.txt`.

```shellsession
$ petname -h
Generate human readable random names

Usage: petname [OPTIONS] [COMMAND]

Commands:
  completions  Print a shell completion script to standard output
  help         Print this message or the help of the given subcommand(s)

Options:
  -w, --words <WORDS>             Number of words in name [default: 2]
  -s, --separator <SEP>           Separator between words [default: -]
      --language <LANG>           Language to generate names in [default: english] [alias: --lang] [possible values: english, luxembourgish, turkish]
      --lists <LIST>              Use the built-in word lists with small, medium, or large words [default: medium] [possible values: small, medium, large]
  -c, --complexity <NUM>          Alias for compatibility with upstream; prefer --lists instead
  -d, --dir <DIR>                 Use custom word lists by specifying a directory containing `adjectives.txt`, `adverbs.txt`, and `nouns.txt`
      --count <COUNT>             Generate multiple names; or use --stream to generate continuously [default: 1]
      --stream                    Stream names continuously
  -l, --letters <LETTERS>         Maximum number of letters in each word; 0 for unlimited [default: 0]
  -a, --alliterate                Generate names where each word begins with the same letter
  -A, --alliterate-with <LETTER>  Generate names where each word begins with the given letter
  -u, --ubuntu                    Alias for compatibility with upstream; prefer --alliterate instead
      --seed <SEED>               Seed the RNG with this value (unsigned 64-bit integer in base-10)
  -h, --help                      Print help (see more with '--help')
  -V, --version                   Print version

Based on Dustin Kirkland's petname project <https://github.com/dustinkirkland/petname>.

$ petname
unified-platypus

$ petname -s _ -w 3
lovely_notable_rooster
```

### Languages

Beyond the default English word lists, rust-petname can generate names in other
languages with their own grammar-aware generators. These are gated behind
per-language features (so the default build stays small) and selected with
`--language`.

Turkish is available via the `lang-turkish` feature. It is grammatically simple
for this purpose – no gender, no adjective agreement, adjective-before-noun
order – and it models Turkish's distinctive emphatic reduplication
(_pekiştirme_), so a two-word name may intensify the adjective morphologically:

```console
$ petname --language turkish --words 2
kıpkırmızı-kedi

$ petname --language turkish --words 3
çok-güzel-yıldız
```

Luxembourgish is available via the `lang-luxembourgish` feature. It has
grammatical gender, so the adjective agrees with the noun (masculine _-en_,
feminine bare, neuter _-t_), and it applies the Eifeler Regel – a final _-n_ or
_-nn_ drops before most consonants but is kept before _n, d, t, z, h_ and vowels
– at the join between words:

```console
$ petname --language luxembourgish --words 2
grousse-mupp     # masculine: groussen + mupp, -n elided
groussen-hond    # masculine: -n kept before h
grouss-kaz       # feminine: bare form

$ petname --language luxembourgish --words 3
ganz-schéint-haus
```

⚠️ The built-in Luxembourgish word lists are a best-effort seed authored by a
non-native speaker, and are awaiting a native-speaker pass – the noun genders in
particular. The generator's rules are sound; it is the data that may not be.
Corrections to [`words/luxembourgish/`](words/luxembourgish/) are very welcome.

More languages (French, German, …) are planned. Each is a distinct generator, so
languages with grammatical gender, agreement, or word-order rules can be
modelled properly rather than approximated.

### Shell completions

`petname completions <SHELL>` prints a completion script to standard output, for
`bash`, `zsh`, `fish`, `elvish`, or `powershell`. Redirect it to the location
your shell loads completions from, for example:

```shellsession
$ petname completions zsh > ~/.zfunc/_petname
$ petname completions bash > /etc/bash_completion.d/petname
$ petname completions fish > ~/.config/fish/completions/petname.fish
```

This is an ordinary subcommand, so `petname` on its own still generates names as
usual. If you installed via Homebrew, the completions are set up for you.

### Performance

This implementation is considerably faster than the upstream `petname`:

```shellsession
$ time /usr/bin/petname
fit-lark

real    0m0.038s
user    0m0.032s
sys     0m0.008s

$ time target/release/petname
contiguous-seriema

real    0m0.004s
user    0m0.001s
sys     0m0.002s
```

These timings are irrelevant if you only need to name a single thing, but if you
need to generate 100s or 1000s of names then rust-petname is handy:

```shellsession
$ time { for i in $(seq 1000); do /usr/bin/petname; done; } > /dev/null

real    0m32.058s
user    0m29.360s
sys     0m5.163s

$ time { for i in $(seq 1000); do target/release/petname; done; } > /dev/null

real    0m2.293s
user    0m1.044s
sys     0m1.003s
```

To be fair, `/usr/bin/petname` is a shell script. The Go command-line version
(available from the golang-petname package on Ubuntu) is comparable to the Rust
version for speed, but has very limited options compared to its shell-script
ancestor and to rust-petname.

Lastly, rust-petname has a `--count` option that speeds up generation of names
considerably:

```shellsession
$ time target/release/petname --count=10000000 > /dev/null

real    0m0.785s
user    0m0.767s
sys     0m0.016s
```

That's ~408,000 (four hundred and eight thousand) times faster, for about 12.7
million petnames a second on this hardware. This is useful if you want to apply
an external filter to the names being generated:

```shellsession
$ petname --words=3 --stream | grep 'love.*\bsalmon$'
```

## Library

You can use rust-petname in your own Rust projects with `cargo add petname`.

## Features & `no_std` support

There are features that can be selected, and many than can be _deselected_
(since they're enabled by default):

- `default-rng` enables `std` and `std_rng` in [rand][]. A couple of convenience
  functions depend on this for a default RNG.
- `default-words` enables the default word lists. Deselecting this will reduce
  the size of compiled artifacts.
- `clap` enables the [clap][] command-line argument parser, which is needed to
  build the `petname` binary.
  - **NOTE** that `clap` is **not** necessary for the library at all, and you
    can deselect it, but it is presently a default feature since otherwise it's
    inconvenient to build the binary. This will probably change in the future.
- `macros` enables the `english!` macro (and its `petnames!` alias). It's
  required for the `default-words` feature, but otherwise it can be deselected.
- `lang-turkish` (not a default) compiles the Turkish generator and enables
  `--language turkish`. Like the English lists, the built-in Turkish word lists
  are embedded only when `default-words` is also enabled. See
  [Languages](#languages).
- `lang-luxembourgish` (not a default) compiles the Luxembourgish generator and
  enables `--language luxembourgish`, on the same terms as `lang-turkish`. See
  [Languages](#languages).

All of the default features are required to build the command-line utility.

The library can be built without any default features, and it will work in a
[`no_std`][no_std] environment, like [Wasm][]. You'll need to figure out a
source of randomness, but [SmallRng::seed_from_u64][smallrng::seed_from_u64] may
be a good starting point.

[rand]: https://crates.io/crates/rand
[clap]: https://crates.io/crates/clap
[no_std]: https://doc.rust-lang.org/reference/crates-and-source-files.html#preludes-and-no_std
[wasm]: https://webassembly.org/
[smallrng::seed_from_u64]: https://docs.rs/rand/latest/rand/rngs/struct.SmallRng.html#method.seed_from_u64

## Upgrading

Releases within a major version are additive, so nothing is required to upgrade
between them; the notes below cover the major transitions only.

### <a id="upgrading-from-2x"></a>From 2.x to 3.x

Version 3.0 brings a few breaking changes to the API, but the command-line is
largely unchanged.

#### Command-line

- One subtle change: if you use `--seed`, the generated names will differ from
  2.x, since `rand` 0.10 produces different output for the same seed.

#### Library

- The `rand` dependency has been bumped from 0.9 to 0.10. If you depend on
  `rand` types (e.g. `RngCore`, `SmallRng`) directly in your own code, you will
  need to upgrade your `rand` dependency to match.
- The `Generator` trait has changed significantly:
  - The `generate` and `generate_one` methods are gone.
  - The trait no longer has a lifetime parameter; `Generator<'a>` is now just
    `Generator`.
  - The one required method is `generate_into`.
  - The `iter` method has been renamed to `namer`. It now returns a [`Namer`]
    directly instead of `Box<dyn Iterator<Item = String>>`, so there is no heap
    allocation for the iterator itself.
- [`Namer`] is a new public type (an iterator over generated petnames). It is
  generic over the generator type, so `Namer<Petnames>` and
  `Namer<Alliterations>` are both valid.
- The built-in word lists are now compiled into the library via the `petnames!`
  proc macro rather than via `build.rs`. This is mostly an internal change, but
  it does mean that the `petname-macros` crate is a new compile-time dependency
  when the `default-words` or `macros` features are enabled.
- A new `macros` feature flag exposes the `petnames!` proc macro as public API.
  It is enabled by default. You can use it to embed custom word lists at compile
  time:
  ```rust
  let p = petname::petnames!("path/to/my/words");
  ```

### <a id="upgrading-from-1x"></a>From 1.x to 2.x

Version 2.0 brought several breaking changes to both the API and the
command-line too. Below are the most important:

#### Command-line

- The `--complexity <COMPLEXITY>` option has been replaced by `--lists <LISTS>`.
  - For compatibility, `--complexity [0,1,2]` will still work, but its
    availability is not shown in the `-h|--help` text.
  - The default is now "medium" (equivalent to `--complexity 1`). Previously it
    was "small" (`--complexity 0`).
- When using custom word lists with `--dir <DIR>`, nouns are now found in a file
  named appropriately `DIR/nouns.txt`. Previously this was `names.txt` but this
  was confusing; the term "names" is overloaded enough already.
  - For compatibility, if `nouns.txt` is not found, an attempt will be made to
    load nouns from `names.txt`.
- The option `--count 0` is no longer a synonym for `--stream`. Use `--stream`
  instead. It's not an error to pass `--count 0`, but it will result in zero
  names being generated.
- The `--non-repeating` flag is no longer recognised ([#101]).

#### Library

- Feature flags have been renamed:
  - `std_rng` is now `default-rng`,
  - `default_dictionary` is now `default-words`.
- The `names` field on the `Petnames` struct has been renamed to `nouns`.
- `Petnames::new()` is now `Petnames::default()`.
- `Petnames::new(…)` now accepts word lists as strings.
- `Names` is no longer public. This served as the iterator struct returned by
  `Petnames::iter(…)`, but this now hides the implementation details by
  returning `impl Iterator<Item = String>` instead. This also means that
  `Names::cardinality(&self)` is no longer available; use
  `Petnames::cardinality(&self, words: u8)` instead.
- `Petnames::iter_non_repeating` has been removed ([#101]).
- `Petnames::generate`, `Petnames::generate_one`, and `Petnames::iter` have been
  extracted into a `Generator` trait. This must be in scope in order to call
  those methods ([#102]).
- The default word lists are now the "medium" lists.

[#101]: https://github.com/allenap/rust-petname/pull/101
[#102]: https://github.com/allenap/rust-petname/pull/102

## Developing & Contributing

To hack the source, [install Cargo][install-cargo], [install
`cargo-hack`][install-cargo-hack], clone this repository, then:

```shellsession
# Build with every combination of feature flags:
$ cargo hack --workspace --feature-powerset build

# Optionally, hide noise when using `git blame`:
$ git config blame.ignoreRevsFile .git-blame-ignore-revs
```

[install-cargo]: https://crates.io/install
[install-cargo-hack]: https://crates.io/crates/cargo-hack#installation

### Making changes & running tests

After installing the source (see above), make changes and:

```shellsession
# Run tests with all feature flag combinations:
cargo hack --workspace --feature-powerset test

# Run Clippy from time to time too:
$ cargo clippy

# Before committing, format:
$ cargo fmt
```

### Making a release

1. Bump version in [`Cargo.toml`]Cargo.toml.
1. Update the dependency on `petnames-macros` too.
1. Paste updated `-h` output into [`README.md`]README.md (this file; see near
   the top). On macOS the command `cargo run --all-features -- -h | pbcopy` is
   helpful. **Note** that `--help` output is not the same as `-h` output: it's
   more verbose and too much for an overview.
1. Build **and** test all crates in the workspace. Testing on its own does build
   code, but a test build can hide warnings about dead code, so do both. To test
   feature combinations, install [cargo-hack][] first, then:
   - `cargo hack --workspace --feature-powerset build`
   - `cargo hack --workspace --feature-powerset test`
1. Check that the crate that would be published is complete:
   `cargo package --workspace --all-features`. This packs each crate, unpacks it
   again, and builds _that_, so it fails if anything the build needs is missing
   from the archive — in practice the `words/` lists, which the macros read from
   disk at compile time. **Note** that `--all-features` is what gives this teeth:
   the default features only reach `words/{small,medium,large}`, so without it a
   missing `words/turkish` or `words/luxembourgish` would sail through. CI runs
   the same check.
1. Commit with message "Bump version to `$VERSION`", e.g. "Bump version to
   3.2.0".
1. Tag with "v`$VERSION`", e.g. `git tag v1.0.10`.
1. Push: `git push && git push --tags`.
1. Publish: `cargo publish --workspace`.

[cargo-hack]: https://crates.io/crates/cargo-hack

## License

This project is licensed under the Apache 2.0 License. See the
[LICENSE](LICENSE) file for details.