1use sha2::{Digest, Sha256};
26
27const ADJECTIVES_A: [&str; 40] = [
28 "bold", "bouncy", "brave", "breezy", "calm", "cheerful", "clever", "curious", "daring",
29 "eager", "elegant", "fierce", "gentle", "graceful", "humble", "jolly", "jovial", "keen",
30 "kind", "lively", "lucky", "mellow", "merry", "nimble", "noble", "plucky", "proud", "quiet",
31 "quirky", "radiant", "silly", "sleepy", "spry", "sturdy", "tranquil", "upbeat", "vivid",
32 "wise", "witty", "zealous",
33];
34
35const ADJECTIVES_B: [&str; 40] = [
36 "amber",
37 "azure",
38 "bronze",
39 "coral",
40 "crimson",
41 "cyan",
42 "emerald",
43 "golden",
44 "green",
45 "indigo",
46 "ivory",
47 "jade",
48 "lavender",
49 "lilac",
50 "magenta",
51 "maroon",
52 "mauve",
53 "navy",
54 "olive",
55 "orange",
56 "peach",
57 "pink",
58 "plum",
59 "purple",
60 "red",
61 "rust",
62 "ruby",
63 "sage",
64 "salmon",
65 "scarlet",
66 "sienna",
67 "silver",
68 "slate",
69 "tan",
70 "teal",
71 "turquoise",
72 "violet",
73 "yellow",
74 "blue",
75 "copper",
76];
77
78const NOUNS: [&str; 40] = [
79 "antelope",
80 "badger",
81 "beetle",
82 "bison",
83 "cricket",
84 "dolphin",
85 "eagle",
86 "elk",
87 "falcon",
88 "ferret",
89 "flamingo",
90 "fox",
91 "gazelle",
92 "gecko",
93 "hare",
94 "heron",
95 "ibex",
96 "iguana",
97 "lynx",
98 "marten",
99 "mongoose",
100 "moose",
101 "narwhal",
102 "orca",
103 "otter",
104 "owl",
105 "panther",
106 "pelican",
107 "penguin",
108 "rabbit",
109 "raven",
110 "salamander",
111 "seal",
112 "sparrow",
113 "tiger",
114 "toucan",
115 "walrus",
116 "weasel",
117 "wolf",
118 "wombat",
119];
120
121pub fn petname(node_id: &str) -> String {
128 let digest = Sha256::digest(node_id.to_ascii_lowercase().as_bytes());
129 let a = ADJECTIVES_A[u16::from_be_bytes([digest[0], digest[1]]) as usize % ADJECTIVES_A.len()];
130 let b = ADJECTIVES_B[u16::from_be_bytes([digest[2], digest[3]]) as usize % ADJECTIVES_B.len()];
131 let n = NOUNS[u16::from_be_bytes([digest[4], digest[5]]) as usize % NOUNS.len()];
132 let suffix = u16::from_be_bytes([digest[6], digest[7]]) % 10_000;
133 format!("{a}_{b}_{n}_{suffix:04}")
134}
135
136#[cfg(test)]
137mod tests {
138 use super::petname;
139
140 #[test]
141 fn petname_is_deterministic_and_shaped() {
142 let id = "7374b0cfab4eea68e271c3815a0f78e21e913397f67345f337ddba7a3a88ab3a";
143 let first = petname(id);
144 assert_eq!(petname(id), first);
145 let parts: Vec<&str> = first.split('_').collect();
146 assert_eq!(parts.len(), 4, "adjective_color_animal_suffix");
147 assert_eq!(parts[3].len(), 4, "zero-padded four-digit suffix");
148 assert!(parts[3].chars().all(|c| c.is_ascii_digit()));
149 }
150}