use super::Error; use nanoid_dictionary::ALPHANUMERIC; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; use std::str::FromStr; pub const INVALID_USERNAME_ERROR: &str = "Invalid username. Pattern is [a-zA-Z0-9]"; #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct Username(pub String); impl Display for Username { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } impl AsRef for Username { fn as_ref(&self) -> &str { self.0.as_ref() } } impl From<&Username> for String { fn from(value: &Username) -> Self { value.to_string() } } impl FromStr for Username { type Err = Error; fn from_str(s: &str) -> Result { if s.chars().all(|c| ALPHANUMERIC.contains(&c)) { Ok(Self(s.to_string())) } else { Err(Error::Invalid("Username")) } } } #[cfg(test)] mod tests { use super::Username; use std::str::FromStr; #[test] fn invalid_characters() { let value = "#!👎 INVALID 🚫!#"; assert!(Username::from_str(value).is_err()); } #[test] fn valid_username() { let value = "philt3r"; let id = Username::from_str(value); assert!(id.is_ok()); let id = id.unwrap(); assert_eq!(id.0, value); } }