lexis/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//
// lexis: Generates human-readable sequences from numeric values using a predefined word list
// src/lib.rs: Common utility functions
//
// Copyright (c) 2024 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0-or-later

/// Predefined word lists
pub mod list;

use std::{
    collections::hash_map::DefaultHasher,
    hash::{Hash, Hasher},
};

use crate::list::{ADJECTIVES, NAMES, WORDS};

/// Provides functionality to convert numeric values to reproducible, human-readable names.
pub trait ToName {
    fn to_name(&self) -> String;
}

/// Trait to be implemented by unsigned integers to convert to human-readable string.
pub trait ToWordSequence {
    fn to_word_sequence(&self) -> String;
}

/// Helper function to convert an unsigned number to a name sequence using predefined adjectives and names.
fn number_to_name<U: Into<u64>>(number: U) -> String {
    // Convert the input number into a u64 and hash it for a uniform distribution.
    let num = hash_number(number.into());

    // Calculate indexes for the adjective and name using the hash value.
    // The modulo operation ensures that the index is within the bounds of the lists.
    let adjective_index = (num % (ADJECTIVES.len() as u64)) as usize;
    let name_index = (num / (ADJECTIVES.len() as u64) % (NAMES.len() as u64)) as usize;

    // Construct the human-readable name by concatenating an adjective and a name from the lists.
    format!("{}_{}", ADJECTIVES[adjective_index], NAMES[name_index])
}

/// Helper function to convert an unsigned number to a word sequence.
fn number_to_words<U: Into<u64>>(number: U) -> String {
    // Convert the input number into a u64 and hash it for a uniform distribution.
    let mut num = hash_number(number.into());
    let mut words = Vec::new();
    while num > 0 {
        let index = (num % 2048) as usize;
        words.push(WORDS[index]);
        num /= 2048;
    }
    words.reverse();
    words.join(" ")
}

/// Uses the DefaultHasher to hash an u64 number.
fn hash_number(number: u64) -> u64 {
    let mut hasher = DefaultHasher::new();
    number.hash(&mut hasher);
    hasher.finish()
}

/// Macro to implement `ToName` for common unsigned integer types.
macro_rules! impl_to_name {
    ($($t:ty),*) => {
        $(impl ToName for $t {
            fn to_name(&self) -> String {
                number_to_name(*self)
            }
        })*
    };
}

/// Macro to implement `ToWordSequence` for common unsigned integer types.
macro_rules! impl_to_word_sequence {
    ($($t:ty),*) => {
        $(impl ToWordSequence for $t {
            fn to_word_sequence(&self) -> String {
                number_to_words(*self)
            }
        })*
    };
}

// Implement `ToName` for common Rust unsigned integer types.
impl_to_name!(u8, u16, u32, u64);

// Implement `ToWordSequence` for common Rust unsigned integer types.
impl_to_word_sequence!(u8, u16, u32, u64);