97 lines
2.6 KiB
Rust
97 lines
2.6 KiB
Rust
//! The one home of the session-handle wordlists (#53).
|
|
//!
|
|
//! Previously duplicated in `web/src/names.ts`; every client now gets handles
|
|
//! from here, served by `GET /api/names`.
|
|
|
|
/// Musical adjectives — tempo, dynamics, and articulation markings.
|
|
pub const ADJECTIVES: &[&str] = &[
|
|
"allegro",
|
|
"andante",
|
|
"adagio",
|
|
"presto",
|
|
"vivace",
|
|
"largo",
|
|
"grave",
|
|
"lento",
|
|
"moderato",
|
|
"crescendo",
|
|
"staccato",
|
|
"legato",
|
|
"forte",
|
|
"dolce",
|
|
"agitato",
|
|
"cantabile",
|
|
"sostenuto",
|
|
"tremolo",
|
|
"rubato",
|
|
"marcato",
|
|
"brillante",
|
|
"maestoso",
|
|
"grazioso",
|
|
"espressivo",
|
|
"tranquillo",
|
|
"animato",
|
|
"spirito",
|
|
"sotto",
|
|
"vivo",
|
|
"feroce",
|
|
];
|
|
|
|
/// Musical nouns — instruments and forms from the concert hall.
|
|
pub const NOUNS: &[&str] = &[
|
|
"cello", "violin", "viola", "oboe", "flute", "clarinet", "bassoon", "trumpet", "trombone",
|
|
"tuba", "horn", "timpani", "harp", "piano", "organ", "lyre", "lute", "fugue", "sonata",
|
|
"octave", "chord", "cadence", "prelude", "rondo", "nocturne", "scherzo", "refrain", "coda",
|
|
"motif", "overture",
|
|
];
|
|
|
|
/// Upper bound for a `GET /api/names?count=N` batch.
|
|
pub const MAX_BATCH: usize = 100;
|
|
|
|
/// A fresh `adjective-noun` handle, e.g. `allegro-cello`.
|
|
pub fn generate() -> String {
|
|
use rand::seq::SliceRandom;
|
|
let mut rng = rand::thread_rng();
|
|
format!(
|
|
"{}-{}",
|
|
ADJECTIVES.choose(&mut rng).unwrap(),
|
|
NOUNS.choose(&mut rng).unwrap()
|
|
)
|
|
}
|
|
|
|
/// `count` fresh handles (the caller clamps; this only trusts its input).
|
|
pub fn generate_batch(count: usize) -> Vec<String> {
|
|
(0..count).map(|_| generate()).collect()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn generated_name_is_adjective_dash_noun() {
|
|
let name = generate();
|
|
let (adj, noun) = name.split_once('-').expect("name contains a dash");
|
|
assert!(ADJECTIVES.contains(&adj), "{adj} not in ADJECTIVES");
|
|
assert!(NOUNS.contains(&noun), "{noun} not in NOUNS");
|
|
}
|
|
|
|
#[test]
|
|
fn batch_returns_the_requested_count() {
|
|
assert_eq!(generate_batch(1).len(), 1);
|
|
assert_eq!(generate_batch(50).len(), 50);
|
|
for name in generate_batch(10) {
|
|
let (adj, noun) = name.split_once('-').unwrap();
|
|
assert!(ADJECTIVES.contains(&adj) && NOUNS.contains(&noun));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn rerolls_can_differ() {
|
|
// 30x30=900 combinations; 20 draws all matching has probability ~1e-40.
|
|
let first = generate();
|
|
let differs = (0..20).any(|_| generate() != first);
|
|
assert!(differs);
|
|
}
|
|
}
|