joyful 0.1.1

Generate delightful, random word combinations - Rust port of the joyful TypeScript library
Documentation
use rand::seq::IndexedRandom;
use std::collections::HashSet;

use crate::{
    constraints::MIN_CATEGORY_WORD_LENGTH,
    words::{CATEGORIES, PREFIXES},
};

pub fn get_prefix_with_budget(max_length: usize) -> Option<String> {
    let candidates: Vec<String> = PREFIXES
        .iter()
        .filter(|w| w.len() <= max_length)
        .map(|w| w.to_string())
        .collect();

    candidates.choose(&mut rand::rng()).cloned()
}

pub fn get_unique_word_with_budget(acc: &HashSet<String>, max_length: usize) -> Option<String> {
    let candidates: Vec<String> = CATEGORIES
        .iter()
        .filter(|word| word.len() <= max_length && !acc.contains(*word))
        .map(|w| w.to_string())
        .collect();

    candidates.choose(&mut rand::rng()).cloned()
}

pub fn get_unique_category(acc: &HashSet<String>) -> String {
    loop {
        let word = CATEGORIES.choose(&mut rand::rng()).unwrap().to_owned();

        if !acc.contains(&word) {
            return word;
        }
    }
}

pub fn calc_budget(
    max_length: usize,
    used_length: usize,
    words_remaining: usize,
    include_separator: bool,
) -> usize {
    let reserved = words_remaining * (MIN_CATEGORY_WORD_LENGTH + 1);
    let separator_cost = if include_separator { 1 } else { 0 };
    max_length.saturating_sub(used_length + separator_cost + reserved)
}