#[macro_use]
extern crate anyhow;
use anyhow::{Context, Result};
use data::{
adjectives::ADJECTIVES, animals::ANIMALS, names::NAMES, personal_nouns::PERSONAL_NOUNS,
places::PLACES, verbs::VERBS,
};
use uuid::Uuid;
mod data;
const NORMAL: [u8; 12] = [12, 11, 14, 13, 13, 10, 12, 11, 14, 5, 6, 7];
const SHORT: [u8; 5] = [6, 6, 7, 8, 5];
fn to_bits(bytes: &[u8]) -> Vec<u8> {
let mut bits: Vec<u8> = Vec::with_capacity(128);
for b in bytes {
bits.extend(u16_to_bits(*b as u16, 8));
}
bits
}
fn to_bits_parted(bytes: &[u16]) -> Vec<u8> {
let mut bits: Vec<u8> = Vec::with_capacity(128);
for (i, b) in bytes.iter().enumerate() {
bits.extend(u16_to_bits(*b, NORMAL[i]));
}
bits
}
#[inline]
fn u16_to_bits(mut b: u16, length: u8) -> Vec<u8> {
let mut bits = Vec::with_capacity(length as usize);
for _ in 0..length {
bits.push((b % 2) as u8);
b >>= 1;
}
bits.reverse();
bits
}
fn to_byte(bits: &[u8]) -> u16 {
let mut _byte = 0u16;
for b in bits {
_byte = 2 * _byte + *b as u16;
}
_byte
}
fn partition(parts: &[u8], bytes: &[u8]) -> [usize; 12] {
let mut bits: Vec<u8> = to_bits(bytes);
let mut _bytes: [usize; 12] = [0; 12];
for (idx, p) in parts.iter().enumerate() {
let tmp = bits.drain(0..(*p as usize));
_bytes[idx] = to_byte(tmp.as_slice()) as usize;
}
_bytes
}
fn de_partition(bits: &[u8]) -> [u8; 16] {
let mut bytes = [0; 16];
for i in 0..16 {
bytes[i] = to_byte(&bits[8 * i..8 * (i + 1)]) as u8;
}
bytes
}
#[inline]
fn _generate(uuid: &Uuid) -> String {
let uuid = uuid.as_bytes();
let words = partition(&NORMAL, uuid);
format!(
"{} {} {} the {} of {} {} {} {} {} and {} {} {}",
NAMES[words[0]],
NAMES[words[1]],
NAMES[words[2]],
PERSONAL_NOUNS[words[3]],
PLACES[words[4]],
VERBS[words[5]],
NAMES[words[6]],
NAMES[words[7]],
NAMES[words[8]],
words[9],
ADJECTIVES[words[10]],
ANIMALS[words[11]]
)
}
pub fn generate() -> String {
let uuid = Uuid::new_v4();
_generate(&uuid)
}
pub fn generate_from(uuid: Uuid) -> String {
_generate(&uuid)
}
pub fn generate_inverse<S: AsRef<str>>(sentence: S) -> Result<Uuid> {
let splitted: Vec<&str> = sentence.as_ref().split(' ').collect();
if splitted.len() < 15 {
return Err(anyhow!(
"The sentence does not correspond to a one from uuid-readable-rs."
));
}
let index_values = [
NAMES
.iter()
.position(|&r| r == splitted[0])
.context("NAMES (0) not found")? as u16,
NAMES
.iter()
.position(|&r| r == splitted[1])
.context("NAMES (1) not found")? as u16,
NAMES
.iter()
.position(|&r| r == splitted[2])
.context("NAMES (2) not found")? as u16,
PERSONAL_NOUNS
.iter()
.position(|&r| r == splitted[4])
.context("PERSONAL_NOUNS (4) not found")? as u16,
PLACES
.iter()
.position(|&r| r == splitted[6])
.context("PLACES (6) not found")? as u16,
VERBS
.iter()
.position(|&r| r == splitted[7])
.context("VERBS (7) not found")? as u16,
NAMES
.iter()
.position(|&r| r == splitted[8])
.context("NAMES (8) not found")? as u16,
NAMES
.iter()
.position(|&r| r == splitted[9])
.context("NAMES (9) not found")? as u16,
NAMES
.iter()
.position(|&r| r == splitted[10])
.context("NAMES (10) not found")? as u16,
splitted[12].parse::<u16>()?,
ADJECTIVES
.iter()
.position(|&r| r == splitted[13])
.context("ADJECTIVES (13) not found")? as u16,
ANIMALS
.iter()
.position(|&r| r == splitted[14])
.context("ANIMALS (14) not found")? as u16,
];
let bits = to_bits_parted(&index_values);
let bytes = de_partition(&bits);
Ok(Uuid::from_slice(&bytes)?)
}
#[inline]
fn _short(uuid: &Uuid) -> String {
let uuid = uuid.as_bytes();
let words = partition(&SHORT, uuid);
format!(
"{} {} by {} {} {}",
NAMES[words[0]], VERBS[words[1]], words[2], ADJECTIVES[words[3]], ANIMALS[words[4]],
)
}
pub fn short() -> String {
let uuid = Uuid::new_v4();
_short(&uuid)
}
pub fn short_from(uuid: Uuid) -> String {
_short(&uuid)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_adjectives_sanity() {
let mut tmp_vec = ADJECTIVES.to_vec();
for x in &tmp_vec {
assert!(!x.contains(" "));
}
let original_length = tmp_vec.len();
tmp_vec.sort();
tmp_vec.dedup();
let final_length = tmp_vec.len();
assert_eq!(original_length, final_length);
}
#[test]
fn test_animals_sanity() {
let mut tmp_vec = ANIMALS.to_vec();
for x in &tmp_vec {
assert!(!x.contains(" "));
}
let original_length = tmp_vec.len();
tmp_vec.sort();
tmp_vec.dedup();
let final_length = tmp_vec.len();
assert_eq!(original_length, final_length);
}
#[test]
fn test_names_sanity() {
let mut tmp_vec = NAMES.to_vec();
for x in &tmp_vec {
assert!(!x.contains(" "));
}
let original_length = tmp_vec.len();
tmp_vec.sort();
tmp_vec.dedup();
let final_length = tmp_vec.len();
assert_eq!(original_length, final_length);
}
#[test]
fn test_personal_nouns_sanity() {
let mut tmp_vec = PERSONAL_NOUNS.to_vec();
for x in &tmp_vec {
assert!(!x.contains(" "));
}
let original_length = tmp_vec.len();
tmp_vec.sort();
tmp_vec.dedup();
let final_length = tmp_vec.len();
assert_eq!(original_length, final_length);
}
#[test]
fn test_places_sanity() {
let mut tmp_vec = PLACES.to_vec();
for x in &tmp_vec {
assert!(!x.contains(" "));
}
let original_length = tmp_vec.len();
tmp_vec.sort();
tmp_vec.dedup();
let final_length = tmp_vec.len();
assert_eq!(original_length, final_length);
}
#[test]
fn test_verbs_sanity() {
let mut tmp_vec = VERBS.to_vec();
for x in &tmp_vec {
assert!(!x.contains(" "));
}
let original_length = tmp_vec.len();
tmp_vec.sort();
tmp_vec.dedup();
let final_length = tmp_vec.len();
assert_eq!(original_length, final_length);
}
#[test]
fn test_generate() {
let uuid = Uuid::parse_str("0ee001c7-12f3-4b29-a4cc-f48838b3587a").unwrap();
let g = generate_from(uuid);
assert_eq!(
g,
"Purdy Fusco Kask the loki of Manteo observed Barbe Lehet Pardew and 26 hard herons"
);
}
#[test]
fn test_short() {
let uuid = Uuid::parse_str("0ee001c7-12f3-4b29-a4cc-f48838b3587a").unwrap();
let s = short_from(uuid);
assert_eq!(s, "Egidius filled by 0 calm hawks");
}
#[test]
fn test_inverse() {
let uuid = Uuid::parse_str("0ee001c7-12f3-4b29-a4cc-f48838b3587a").unwrap();
let i = generate_inverse(&generate_from(uuid)).unwrap();
assert_eq!(i, uuid);
}
#[test]
fn test_bits_conversion() {
let arr = [41];
let bits = to_bits(&arr);
assert_eq!(bits, vec![0, 0, 1, 0, 1, 0, 0, 1]);
let byte = to_byte(&bits);
assert_eq!(byte, 41);
}
#[test]
fn test_compatibility() {
let uuid = Uuid::parse_str("00000000-0000-0000-0000-000000000000").unwrap();
let zeroed = generate_from(uuid);
assert_eq!(zeroed, "Fusco Fusco Fusco the muleteer of Katy suspended Fusco Fusco Fusco and 0 mysterious rooks");
let uuid = Uuid::parse_str("ffffffff-ffff-ffff-ffff-ffffffffffff").unwrap();
let full = generate_from(uuid);
assert_eq!(full, "Antone Concordia Katharyn the minister of Mosinee trotted Antone Concordia Katharyn and 31 slow hogs");
let uuid = Uuid::parse_str("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF").unwrap();
let strange = generate_from(uuid);
assert_eq!(strange, "Antone Concordia Caravette the minister of Mosinee trotted Antone Concordia Katharyn and 31 slow hogs");
}
#[test]
fn test_bad_inverse() {
let sentence = "109812 ???./ ` the muleteer of Katy suspended Fusco Fusco Fusco and 0 mysterious rooks";
let rev = generate_inverse(sentence);
assert!(rev.is_err());
let sentence = "109812 ???./\0zdqdqz";
let rev = generate_inverse(sentence);
assert!(rev.is_err());
}
}