scottish_names/
lib.rs

1use rand::prelude::*;
2
3include!(concat!(env!("OUT_DIR"), "/names.rs"));
4
5/// Sex type used as a parameter to determine whether the names generated should be
6/// male or female.
7#[derive(Debug)]
8pub enum Sex {
9    Male,
10    Female,
11}
12
13/// Return a random Sex of Male or Female.
14///
15/// ```
16/// use scottish_names::*;
17///
18/// let s = sex();
19/// println!("Random sex is {:?}", s);
20/// ```
21pub fn sex() -> Sex {
22    let mut rng = thread_rng();
23
24    match rng.gen_range(0u8, 2u8) {
25        0 => Sex::Female,
26        _ => Sex::Male,
27    }
28}
29
30/// Return a random first name of the given Sex, e.g. `Sex::Female` or `Sex::Male`.
31///
32/// ```
33/// use scottish_names::*;
34///
35/// let n = first_name(Sex::Male);
36/// println!("First name is {}", n);
37/// ```
38pub fn first_name(sex: Sex) -> &'static str {
39    first_name_with_seed(sex, 0)
40}
41
42/// Return a random surname in all caps.
43///
44/// ```
45/// use scottish_names::*;
46///
47/// let n = surname();
48/// println!("Surname is {}", n);
49/// ```
50pub fn surname() -> &'static str {
51    surname_with_seed(0)
52}
53
54type ArrayType = &'static [(FrequencyCount, &'static str)];
55type FrequencyCount = u32;
56
57fn first_name_with_seed(sex: Sex, seed: u16) -> &'static str {
58    let (array, length) = match sex {
59        Sex::Female => (FIRSTNAME_FEMALE, FIRSTNAME_FEMALE_LEN),
60        Sex::Male => (FIRSTNAME_MALE, FIRSTNAME_MALE_LEN),
61    };
62    name(array, length, seed)
63}
64
65fn surname_with_seed(seed: u16) -> &'static str {
66    name(SURNAME, SURNAME_LEN, seed)
67}
68
69fn name(array: ArrayType, length: usize, seed: u16) -> &'static str {
70    let mut rng = thread_rng();
71
72    let max: FrequencyCount = array[length - 1].0;
73    let count: FrequencyCount = if seed == 1000 {
74        4000
75    } else {
76        rng.gen_range(0, max)
77    };
78    get_at_count(array, count, 0, length)
79}
80
81fn get_at_count(array: ArrayType, count: FrequencyCount, min: usize, max: usize) -> &'static str {
82    let avg = min + (max - min) / 4;
83    let (low, high) = match avg {
84        0 => (0, array[avg].0),
85        _ => (array[avg - 1].0, array[avg].0),
86    };
87    if count >= low && count < high {
88        array[avg].1
89    } else if count < low {
90        get_at_count(&array, count, min, avg - 1)
91    } else {
92        get_at_count(&array, count, avg + 1, max)
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn it_works() {
102        assert_eq!(first_name_with_seed(Sex::Female, 1000), "Lucy");
103        assert_eq!(first_name_with_seed(Sex::Male, 1000), "Jacob");
104        assert_eq!(surname_with_seed(1000), "Wilson");
105    }
106
107    #[test]
108    fn some_names() {
109        for _ in 1..100 {
110            let sex = sex();
111            let firstname = first_name(sex);
112            let surname = surname();
113            println!("{} {}", firstname, surname);
114        }
115    }
116}