Skip to content

Commit

Permalink
Merge pull request #50 from allenap/features-for-2.0
Browse files Browse the repository at this point in the history
Features for 2.0
  • Loading branch information
allenap authored Sep 11, 2023
2 parents 1f86912 + 1ea3af4 commit a19ca48
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 197 deletions.
9 changes: 8 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@
"Clippy",
"dtolnay",
"itertools",
"pbcopy",
"petname",
"Petnames",
"pname",
"rngs",
"rustfmt",
"rustup"
"rustup",
"Seedable",
"shellsession",
"smallrng",
"struct"
]
}
21 changes: 13 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,24 @@ path = "src/lib.rs"
doc = false
name = "petname"
path = "src/main.rs"
required-features = ["clap", "std_rng", "default_dictionary"]
required-features = ["clap", "default-rng", "default-words"]

[features]
# We include features that must be used for the binary regardless of if they are used (like clap).
default = ["clap", "std_rng", "default_dictionary"]
# `clap` is NOT required for the library but is required for the command-line
# binary. Omitting it from the `default` list means that it must be specified
# _every time_ you want to build the binary, so it's here as a convenience.
default = ["clap", "default-rng", "default-words"]
# Allows generating petnames with thread rng.
std_rng = ["rand/std", "rand/std_rng"]
# Allows the default dictionary to be used.
default_dictionary = []
default-rng = ["rand/std", "rand/std_rng"]
# Allows the default word lists to be used.
default-words = []

[build-dependencies]
anyhow = "^1.0.75"

[dependencies]
clap = { version = "^3.1.0", features = ["cargo", "derive"], optional = true }
itertools = { version = "^0.10.3", default-features = false }
clap = { version = "^4.4.2", features = ["cargo", "derive"], optional = true }
itertools = { version = "^0.11.0", default-features = false }
rand = { version = "^0.8.5", default-features = false }

[package.metadata.docs.rs]
Expand Down
105 changes: 68 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,39 +44,34 @@ The `petname` binary from rust-petname is drop-in compatible with the original
should behave the same.

```shellsession
$ petname --help
rust-petname 1.1.3
Gavin Panella <gavinpanella@gmail.com>
$ petname -h
Generate human readable random names

USAGE:
petname [OPTIONS]

OPTIONS:
-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
-c, --complexity <COM> Use small words (0), medium words (1), or large words (2)
[default: 0]
--count <COUNT> Generate multiple names; pass 0 to produce infinite names
(--count=0 is deprecated; use --stream instead) [default: 1]
-d, --dir <DIR> Directory containing adjectives.txt, adverbs.txt, names.txt
-h, --help Print help information
-l, --letters <LETTERS> Maximum number of letters in each word; 0 for unlimited
[default: 0]
--non-repeating Do not generate the same name more than once
-s, --separator <SEP> Separator between words [default: -]
--stream Stream names continuously
-u, --ubuntu Alias; see --alliterate
-V, --version Print version information
-w, --words <WORDS> Number of words in name [default: 2]
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: small] [possible values: small, medium, large]
-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
--non-repeating Do not generate the same name more than once
-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; see --alliterate
--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
untaunting-paxton
unified-platypus

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

### Performance
Expand Down Expand Up @@ -140,19 +135,29 @@ an external filter to the names being generated:
$ petname --words=3 --stream | grep 'love.*\bsalmon$'
```

## Library

You can use of 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:

- `std_rng` enables `std` and `std_rng` in [rand][].
- `default_dictionary` enables the default word lists.
- `clap` enables the [clap][] command-line argument parser.
- `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.

However, 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
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.

Expand All @@ -162,14 +167,40 @@ be a good starting point.
[wasm]: https://webassembly.org/
[smallrng::seed_from_u64]: https://docs.rs/rand/latest/rand/trait.SeedableRng.html#method.seed_from_u64

## Getting Started
## Upgrading from 1.x

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

- [Install Cargo][install-cargo],
- Install this crate: `cargo install petname`.
### Command-line

- The `--complexity <COMPLEXITY>` option has been replaced by `--lists <LISTS>`.
- 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.
- 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.

### 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`.
Previously the complexity was given as a number – 0, 1, or 2 – but now the
word lists to use are given as a string: small, medium, or large.
- `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.

## Developing & Contributing

Alternatively, to hack the source:
To hack the source:

- [Install Cargo][install-cargo],
- Clone this repository,
Expand All @@ -178,11 +209,11 @@ Alternatively, to hack the source:

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

## Running the tests
### Running the tests

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

## Making a release
### Making a release

1. Bump version in [`Cargo.toml`](Cargo.toml).
2. Paste updated `--help` output into [`README.md`](README.md) (this file; see
Expand Down
30 changes: 22 additions & 8 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,44 @@
use anyhow::{Context, Result};
use std::collections::HashSet;
use std::env;
use std::fs;
use std::path::Path;

fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
fn main() -> Result<()> {
let words_dir = Path::new("words");
let out_dir = env::var_os("OUT_DIR").context("OUT_DIR not set")?;
let dest_path = Path::new(&out_dir).join("words.rs");

let mut lines: Vec<String> = vec![];

let list_sizes = ["small", "medium", "large"];
let list_names = ["adjectives", "adverbs", "names"];
let list_names = ["adjectives", "adverbs", "nouns"];

for list_size in list_sizes {
lines.push(format!("pub mod {list_size} {{"));
for list_name in list_names {
let list_path = format!("words/{list_size}/{list_name}.txt");
println!("cargo:rerun-if-changed={list_path}");
let list_raw = fs::read_to_string(list_path).unwrap();
let list = list_raw.split_whitespace().collect::<Vec<_>>();
let list_path = words_dir.join(list_size).join(list_name).with_extension("txt");
println!("cargo:rerun-if-changed={}", list_path.to_string_lossy());
let list_raw = fs::read_to_string(&list_path)
.with_context(|| format!("Could not read word list from {list_path:?}"))?;
let list = {
// Ensure we have no duplicates.
let words = list_raw.split_whitespace().collect::<HashSet<_>>();
// Collect into a `Vec` and sort it.
let mut list = words.into_iter().collect::<Vec<_>>();
list.sort();
list
};
lines.push(format!(" pub static {}: [&str; {}] = [", list_name.to_uppercase(), list.len()));
lines.extend(list.iter().map(|word| format!(" \"{word}\",")));
lines.push(" ];".to_string());
}
lines.push("}".to_string());
}

fs::write(dest_path, lines.join("\n")).unwrap();
fs::write(&dest_path, lines.join("\n"))
.with_context(|| format!("Could not write word lists to output file {dest_path:?}"))?;
println!("cargo:rerun-if-changed=build.rs");

Ok(())
}
58 changes: 37 additions & 21 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,61 +1,60 @@
use std::path::PathBuf;

use clap::crate_version;
use clap::Parser;
use clap::{Parser, ValueEnum};

/// Generate human readable random names.
#[derive(Parser)]
#[clap(
#[command(
name = "rust-petname",
version = crate_version!(),
version,
author,
after_help = "Based on Dustin Kirkland's petname project <https://github.com/dustinkirkland/petname>."
)]
pub struct Cli {
/// Number of words in name
#[clap(short, long, value_name = "WORDS", default_value_t = 2)]
#[arg(short, long, value_name = "WORDS", default_value_t = 2)]
pub words: u8,

/// Separator between words
#[clap(short, long, value_name = "SEP", default_value = "-")]
#[arg(short, long, value_name = "SEP", default_value = "-")]
pub separator: String,

/// Use small words (0), medium words (1), or large words (2)
#[clap(short, long, value_name = "COM", possible_values = &["0", "1", "2"], default_value_t = 0, hide_possible_values = true)]
pub complexity: u8,
/// Use the built-in word lists with small, medium, or large words
#[arg(long, value_name = "LIST", default_value_t = WordList::Small)]
pub lists: WordList,

/// Directory containing adjectives.txt, adverbs.txt, names.txt
#[clap(short, long = "dir", value_name = "DIR", conflicts_with = "complexity")]
/// Use custom word lists by specifying a directory containing
/// `adjectives.txt`, `adverbs.txt`, and `nouns.txt`
#[arg(short, long = "dir", value_name = "DIR", conflicts_with = "lists")]
pub directory: Option<PathBuf>,

/// Generate multiple names; pass 0 to produce infinite names
/// (--count=0 is deprecated; use --stream instead)
#[clap(long, value_name = "COUNT", default_value_t = 1)]
/// Generate multiple names; or use --stream to generate continuously
#[arg(long, value_name = "COUNT", default_value_t = 1)]
pub count: usize,

/// Stream names continuously
#[clap(long, conflicts_with = "count")]
#[arg(long, conflicts_with = "count")]
pub stream: bool,

/// Do not generate the same name more than once
#[clap(long)]
#[arg(long)]
pub non_repeating: bool,

/// Maximum number of letters in each word; 0 for unlimited
#[clap(short, long, value_name = "LETTERS", default_value_t = 0)]
#[arg(short, long, value_name = "LETTERS", default_value_t = 0)]
pub letters: usize,

/// Generate names where each word begins with the same letter
#[clap(short, long)]
#[arg(short, long)]
pub alliterate: bool,

/// Generate names where each word begins with the given letter
#[clap(short = 'A', long, value_name = "LETTER")]
#[arg(short = 'A', long, value_name = "LETTER")]
pub alliterate_with: Option<char>,

// For compatibility with upstream.
/// Alias; see --alliterate
#[clap(short, long)]
#[arg(short, long)]
pub ubuntu: bool,

/// Seed the RNG with this value (unsigned 64-bit integer in base-10)
Expand All @@ -65,6 +64,23 @@ pub struct Cli {
/// emitted is not guaranteed across versions of rust-petname because the
/// underlying random number generator in use explicitly does not make that
/// guarantee.
#[clap(long, value_name = "SEED")]
#[arg(long, value_name = "SEED")]
pub seed: Option<u64>,
}

#[derive(Clone, ValueEnum)]
pub enum WordList {
Small,
Medium,
Large,
}

impl std::fmt::Display for WordList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Small => write!(f, "small"),
Self::Medium => write!(f, "medium"),
Self::Large => write!(f, "large"),
}
}
}
Loading

0 comments on commit a19ca48

Please sign in to comment.