use super::Error; use nanoid::nanoid; use nanoid_dictionary::ALPHANUMERIC; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; use std::str::FromStr; const LENGTH: usize = 30; #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct KeyID(pub String); impl Display for KeyID { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } impl Default for KeyID { fn default() -> Self { Self(nanoid!(LENGTH, ALPHANUMERIC)) } } impl AsRef for KeyID { fn as_ref(&self) -> &str { self.0.as_ref() } } impl FromStr for KeyID { type Err = Error; fn from_str(s: &str) -> Result { if s.len() == LENGTH && s.chars().all(|c| ALPHANUMERIC.contains(&c)) { Ok(Self(s.to_string())) } else { Err(Error::Invalid("Key")) } } } #[cfg(test)] mod tests { use super::{KeyID, LENGTH}; use std::str::FromStr; #[test] fn invalid_length() { assert!(KeyID::from_str("test").is_err()); } #[test] fn invalid_characters() { let value = "abcdef!!!!!!@#$%^&*()_++zyxwvq"; assert_eq!(value.len(), LENGTH); assert!(KeyID::from_str(value).is_err()); } #[test] fn valid() { let value = "abcdefghijklmnopqrstuvwxyzabcd"; assert_eq!(value.len(), LENGTH); let id = KeyID::from_str(value); assert!(id.is_ok()); let id = id.unwrap(); assert_eq!(id.0, value); } }