angry_purple_tiger/
lib.rs1use std::{fmt, str::FromStr};
2mod words;
3
4#[derive(Debug, PartialEq)]
5pub struct AnimalName {
6 adjective: &'static str,
7 color: &'static str,
8 animal: &'static str,
9}
10
11impl fmt::Display for AnimalName {
12 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
13 f.write_fmt(format_args!(
14 "{}-{}-{}",
15 self.adjective, self.color, self.animal
16 ))
17 }
18}
19
20impl PartialEq<&str> for AnimalName {
21 fn eq(&self, other: &&str) -> bool {
22 let mut split = (*other).split('-');
23 for entry in &[self.adjective, self.color, self.animal] {
24 if split.next().map_or_else(|| true, |s| &s != entry) {
27 return false;
28 }
29 }
30 true
31 }
32}
33
34fn str_to_animal_name(s: &str) -> AnimalName {
35 let digest = hex_digest(s);
36 AnimalName {
37 adjective: words::ADJECTIVES[digest[0]],
38 color: words::COLORS[digest[1]],
39 animal: words::ANIMALS[digest[2]],
40 }
41}
42
43impl FromStr for AnimalName {
44 type Err = std::convert::Infallible;
45 fn from_str(s: &str) -> Result<Self, Self::Err> {
46 Ok(str_to_animal_name(s))
47 }
48}
49#[cfg(feature = "helium_crypto")]
50impl From<helium_crypto::PublicKey> for AnimalName {
51 fn from(pubkey: helium_crypto::PublicKey) -> Self {
52 str_to_animal_name(&pubkey.to_string())
53 }
54}
55
56fn hex_digest(s: &str) -> [usize; 3] {
57 let digest = md5::compute(s);
58 let mut result = [0usize; 3];
59 compress(digest.0.len() / 3, &digest.0, &mut result);
60 result
61}
62
63fn compress(size: usize, bytes: &[u8], dest: &mut [usize]) {
64 if bytes.len() >= (2 * size) {
65 dest[0] = bytes[0..size].iter().fold(0u8, |acc, b| acc ^ b) as usize;
66 compress(size, &bytes[size..], &mut dest[1..])
67 } else {
68 dest[0] = bytes.iter().fold(0u8, |acc, b| acc ^ b) as usize;
69 }
70}
71
72#[cfg(test)]
73mod test {
74 use super::AnimalName;
75
76 #[test]
77 fn basic() {
78 let known = "112CuoXo7WCcp6GGwDNBo6H5nKXGH45UNJ39iEefdv2mwmnwdFt8";
79 let animal_name = known.parse::<AnimalName>().expect("animal name");
80 assert_eq!(animal_name, "feisty-glass-dalmatian")
81 }
82
83 #[test]
84 #[cfg(feature = "helium-crypto")]
85 fn from_public_key() {
86 use std::str::FromStr;
87 let known = helium_crypto::PublicKey::from_str(
88 "112CuoXo7WCcp6GGwDNBo6H5nKXGH45UNJ39iEefdv2mwmnwdFt8",
89 )
90 .expect("public key");
91 let animal_name: AnimalName = known.into();
92 assert_eq!(animal_name, "feisty-glass-dalmatian")
93 }
94}