use rand::prelude::*;
mod adjectives;
mod adverbs;
mod nouns;
use adjectives::ADJECTIVES;
use adverbs::ADVERBS;
use nouns::NOUNS;
#[derive(Debug, Clone)]
pub struct HaikuOptions<'a> {
pub words: usize,
pub separator: &'a str,
pub digits: usize,
}
impl Default for HaikuOptions<'_> {
fn default() -> Self {
Self {
words: 2,
separator: "-",
digits: 2,
}
}
}
pub fn haiku(options: &HaikuOptions) -> String {
let mut rng = rand::rng();
let words = options.words.max(1);
let mut parts: Vec<String> = Vec::with_capacity(words + 1);
for i in 0..words {
let word = match (words, i) {
(1, 0) => *NOUNS.choose(&mut rng).unwrap(),
(2, 0) => *ADJECTIVES.choose(&mut rng).unwrap(),
(2, 1) => *NOUNS.choose(&mut rng).unwrap(),
(_, 0) => *ADVERBS.choose(&mut rng).unwrap(),
(_, 1) => *ADJECTIVES.choose(&mut rng).unwrap(),
(_, 2) => *NOUNS.choose(&mut rng).unwrap(),
(_, i) if i % 2 == 1 => *ADJECTIVES.choose(&mut rng).unwrap(),
(_, _) => *NOUNS.choose(&mut rng).unwrap(),
};
parts.push(word.to_string());
}
if options.digits > 0 {
let digits = options.digits.min(9);
let max = 10_u32.pow(digits as u32);
let num: u32 = rng.random_range(0..max);
parts.push(format!("{:0width$}", num, width = digits));
}
parts.join(options.separator)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_haiku_default() {
let name = haiku(&HaikuOptions::default());
let parts: Vec<&str> = name.split('-').collect();
assert_eq!(parts.len(), 3);
assert!(!parts[0].is_empty());
assert!(!parts[1].is_empty());
assert_eq!(parts[2].len(), 2); assert!(parts[2].parse::<u32>().is_ok());
}
#[test]
fn test_haiku_no_number() {
let name = haiku(&HaikuOptions {
digits: 0,
..Default::default()
});
let parts: Vec<&str> = name.split('-').collect();
assert_eq!(parts.len(), 2);
assert!(parts.iter().all(|p| p.parse::<u32>().is_err()));
}
#[test]
fn test_haiku_custom_separator() {
let name = haiku(&HaikuOptions {
separator: "_",
..Default::default()
});
assert!(name.contains('_'));
assert!(!name.contains('-'));
}
#[test]
fn test_haiku_three_words() {
let name = haiku(&HaikuOptions {
words: 3,
digits: 0,
..Default::default()
});
let parts: Vec<&str> = name.split('-').collect();
assert_eq!(parts.len(), 3);
}
#[test]
fn test_haiku_four_digits() {
let name = haiku(&HaikuOptions {
digits: 4,
..Default::default()
});
let parts: Vec<&str> = name.split('-').collect();
assert_eq!(parts[2].len(), 4);
}
}