rust-petname
Generate human readable random names.
🚨 Upgrading? Check the notes on switching from 2.x to 3.x and from 1.x to 2.x.
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.
english!(aliased aspetnames!) andturkish!(with featurelang-turkish) macros 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_stdsupport (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.
🍺 Homebrew – You can instead install it from the
allenap/utilstap. The formula is namedrust-petnameand provides the samepetnamecommand:$ brew install allenap/utils/rust-petname
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.
$ 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] [aliases: --lang] [possible values: english, 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:
$ petname --language turkish --words 2
kıpkırmızı-kedi
$ petname --language turkish --words 3
çok-güzel-yıldız
More languages (Luxembourgish, 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:
$ 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:
$ 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:
$ 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:
$ 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:
$ 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-rngenablesstdandstd_rngin rand. A couple of convenience functions depend on this for a default RNG.default-wordsenables the default word lists. Deselecting this will reduce the size of compiled artifacts.clapenables the clap command-line argument parser, which is needed to build thepetnamebinary.- NOTE that
clapis 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.
- NOTE that
macrosenables theenglish!macro (and itspetnames!alias). It's required for thedefault-wordsfeature, 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 whendefault-wordsis also enabled. See 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 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 3.0.0 to 3.0.1
3.0.1 is purely additive – nothing is required to upgrade, and the default build
is unchanged. It adds non-English generators (see Languages). The
new --language flag (alias --lang) selects the language; the lang-turkish
feature compiles a Turkish generator and makes --language turkish available.
The word-list macro is now english!, with petnames! kept as a
backwards-compatible alias, and a turkish! macro is available when the
lang-turkish feature is enabled.
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, sincerand0.10 produces different output for the same seed.
Library
- The
randdependency has been bumped from 0.9 to 0.10. If you depend onrandtypes (e.g.RngCore,SmallRng) directly in your own code, you will need to upgrade yourranddependency to match. - The
Generatortrait has changed significantly:- The
generateandgenerate_onemethods are gone. - The trait no longer has a lifetime parameter;
Generator<'a>is now justGenerator. - The one required method is
generate_into. - The
itermethod has been renamed tonamer. It now returns a [Namer] directly instead ofBox<dyn Iterator<Item = String>>, so there is no heap allocation for the iterator itself.
- The
- [
Namer] is a new public type (an iterator over generated petnames). It is generic over the generator type, soNamer<Petnames>andNamer<Alliterations>are both valid. - The built-in word lists are now compiled into the library via the
petnames!proc macro rather than viabuild.rs. This is mostly an internal change, but it does mean that thepetname-macroscrate is a new compile-time dependency when thedefault-wordsormacrosfeatures are enabled. - A new
macrosfeature flag exposes thepetnames!proc macro as public API. It is enabled by default. You can use it to embed custom word lists at compile time:let p = petnames!;
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|--helptext. - The default is now "medium" (equivalent to
--complexity 1). Previously it was "small" (--complexity 0).
- For compatibility,
- When using custom word lists with
--dir <DIR>, nouns are now found in a file named appropriatelyDIR/nouns.txt. Previously this wasnames.txtbut this was confusing; the term "names" is overloaded enough already.- For compatibility, if
nouns.txtis not found, an attempt will be made to load nouns fromnames.txt.
- For compatibility, if
- The option
--count 0is no longer a synonym for--stream. Use--streaminstead. It's not an error to pass--count 0, but it will result in zero names being generated. - The
--non-repeatingflag is no longer recognised (#101).
Library
- Feature flags have been renamed:
std_rngis nowdefault-rng,default_dictionaryis nowdefault-words.
- The
namesfield on thePetnamesstruct has been renamed tonouns. Petnames::new()is nowPetnames::default().Petnames::new(…)now accepts word lists as strings.Namesis no longer public. This served as the iterator struct returned byPetnames::iter(…), but this now hides the implementation details by returningimpl Iterator<Item = String>instead. This also means thatNames::cardinality(&self)is no longer available; usePetnames::cardinality(&self, words: u8)instead.Petnames::iter_non_repeatinghas been removed (#101).Petnames::generate,Petnames::generate_one, andPetnames::iterhave been extracted into aGeneratortrait. 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, install
cargo-hack, clone this repository, then:
# 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
Making changes & running tests
After installing the source (see above), make changes and:
# 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
- Bump version in
Cargo.toml. - Update the dependency on
petnames-macrostoo. - Paste updated
-houtput intoREADME.md(this file; see near the top). On macOS the commandcargo run --all-features -- -h | pbcopyis helpful. Note that--helpoutput is not the same as-houtput: it's more verbose and too much for an overview. - 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 buildcargo hack --workspace --feature-powerset test
- Commit with message "Bump version to
$VERSION." - Tag with "v
$VERSION", e.g.git tag v1.0.10. - Push:
git push && git push --tags. - Publish:
cargo publish --workspace.
License
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.