use std::fmt::Debug; use rand::prelude::Distribution; use serde::de::{Unexpected, Visitor}; use serde::Deserialize; pub struct ShortCode([ShortCodeChar; N]); impl ShortCode { pub fn as_bytes(&self) -> [u8; N] { self.0.map(|v| v.0 as u8) } } impl Debug for ShortCode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let short_code = String::from_iter(self.0.map(|v| v.0)); f.debug_tuple("ShortCode").field(&short_code).finish() } } impl<'de, const N: usize> Deserialize<'de> for ShortCode { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { struct ShortCodeVisitor; impl<'de, const N: usize> Visitor<'de> for ShortCodeVisitor { type Value = ShortCode; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("a valid shortcode") } fn visit_str(self, v: &str) -> Result where E: serde::de::Error, { if v.len() != N { return Err(E::invalid_length(v.len(), &"a 12 character value")); } if !v.is_ascii() { return Err(E::invalid_value(Unexpected::Str(v), &"ascii only")); } // This is fine, it'll get overwritten anyways. let mut output = [ShortCodeChar('\0'); N]; for (i, c) in v.char_indices() { output[i] = c.try_into().map_err(|_| { E::invalid_value(Unexpected::Char(c), &"a valid short code character") })?; } Ok(ShortCode(output)) } } deserializer.deserialize_str(ShortCodeVisitor) } } /// `ShortCodeChar` uses the Word-safe alphabet, a Base32 extension of the Open /// Location Code Base20 alphabet. #[derive(Clone, Copy, Debug)] struct ShortCodeChar(char); impl<'de> Deserialize<'de> for ShortCodeChar { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { struct ShortCodeCharVisitor; impl<'de> Visitor<'de> for ShortCodeCharVisitor { type Value = ShortCodeChar; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("a valid short code char") } fn visit_char(self, v: char) -> Result where E: serde::de::Error, { v.try_into().map_err(|_| { E::invalid_value(Unexpected::Char(v as char), &"a valid short code character") }) } } deserializer.deserialize_char(ShortCodeCharVisitor) } } impl TryFrom for ShortCodeChar { type Error = &'static str; fn try_from(v: char) -> Result { if v.is_ascii() && ALPHABET.contains(&(v as u8)) { Ok(Self(v)) } else { Err("a valid short code character") } } } pub struct Generator; const ALPHABET: &[u8; 32] = b"23456789CFGHJMPQRVWXcfghjmpqrvwx"; impl Distribution for Generator { fn sample(&self, rng: &mut R) -> ShortCodeChar { let value = rng.gen_range(0..32); assert!(value < 32); ShortCodeChar(ALPHABET[value] as char) } } impl Distribution> for Generator { fn sample(&self, rng: &mut R) -> ShortCode { let mut arr = [ShortCodeChar('\0'); N]; for c in arr.iter_mut() { *c = self.sample(rng); } ShortCode(arr) } }