Skip to content

Generate human readable random names. Rust port of Dustin Kirkland's petname library.

License

Notifications You must be signed in to change notification settings

allenap/rust-petname

Repository files navigation

rust-petname

Generate human readable random names.

🚨 UPGRADING FROM 1.x? There are several breaking changes; please read the notes.

Petnames 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 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, and Golang too.

Notable features:

  • Choose from 3 built-in word lists, or provide your own.
  • 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).
  • Compile without built-in dictionaries to reduce library/binary size.

Command-line utility

If you have installed 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.

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

$ petname -h
Generate human readable random names

Usage: petname [OPTIONS]

Options:
  -w, --words <WORDS>             Number of words in name [default: 2]
  -s, --separator <SEP>           Separator between words [default: -]
      --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

Performance

This implementation is considerably faster than the upstream petname:

$ time /usr/bin/petname
fit-lark

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

$ time target/release/petname
cool-guinea

real    0m0.002s
user    0m0.002s
sys     0m0.000s

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:

$ 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.199s
user    0m1.333s
sys     0m0.987s

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:

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

real    0m1.327s
user    0m1.322s
sys     0m0.004s

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

$ 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 a few features that can be selected – or, more correctly, deselected, since all features are 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.

All of these 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 environment, like Wasm. You'll need to figure out a source of randomness, but SmallRng::seed_from_u64 may be a good starting point.

Upgrading from 1.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.

Developing & Contributing

To hack the source:

  • Install Cargo,
  • Clone this repository,
  • Build it: cargo build.
  • Optionally, hide noise when using git blame: git config blame.ignoreRevsFile .git-blame-ignore-revs.

Running the tests

After installing the source (see above) run tests with: cargo test.

Making a release

  1. Bump version in Cargo.toml.
  2. Paste updated -h output into README.md (this file; see near the top). On macOS the command cargo run -- -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.
  3. Build and test. The latter on its own does do a build, but a test build can hide warnings about dead code, so do both.
    • With default features: cargo build && cargo test
    • Without: cargo build --no-default-features && cargo test --no-default-features
  4. Commit with message "Bump version to $VERSION."
  5. Tag with "v$VERSION", e.g. git tag v1.0.10.
  6. Push: git push && git push --tags.
  7. Publish: cargo publish.

License

This project is licensed under the Apache 2.0 License. See the LICENSE file for details.

Footnotes

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

About

Generate human readable random names. Rust port of Dustin Kirkland's petname library.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages