use super::Error; use nanoid::nanoid; use nanoid_dictionary::NOLOOKALIKES; use std::fmt::{Display, Formatter}; use std::str::FromStr; const LENGTH: usize = 15; #[derive(Debug, Clone, PartialEq)] pub struct UserID(pub String); impl Display for UserID { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } impl Default for UserID { fn default() -> Self { Self(nanoid!(LENGTH, NOLOOKALIKES)) } } impl FromStr for UserID { type Err = Error; fn from_str(s: &str) -> Result { if s.len() == LENGTH && s.chars().all(|c| NOLOOKALIKES.contains(&c)) { Ok(Self(s.to_string())) } else { Err(Error::Invalid("User")) } } } #[cfg(test)] mod tests { use super::{UserID, LENGTH}; use std::str::FromStr; #[test] fn invalid_length() { assert!(UserID::from_str("test").is_err()); } #[test] fn invalid_characters() { let value = "11lI0Oouv5Ss2ZZ"; assert_eq!(value.len(), LENGTH); assert!(UserID::from_str(value).is_err()); } #[test] fn valid() { let value = "nqWxagcdTGGXUgn"; let id = UserID::from_str(value); assert!(id.is_ok()); let id = id.unwrap(); assert_eq!(id.0, value); } }