use std::fmt::{self, Display, Formatter}; use rand::distr::{Alphanumeric, SampleString}; use crate::error::SessionError; pub const MAX_SESSION_KEY_LEN: usize = 4064; pub const SESSION_KEY_LEN: usize = 64; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct SessionKey(String); impl SessionKey { pub fn generate() -> Self { Self(Alphanumeric.sample_string(&mut rand::rng(), SESSION_KEY_LEN)) } pub fn as_str(&self) -> &str { &self.0 } pub fn into_string(self) -> String { self.0 } } impl TryFrom for SessionKey { type Error = SessionError; fn try_from(value: String) -> Result { if value.is_empty() { return Err(SessionError::config("the session key is empty")); } if value.len() > MAX_SESSION_KEY_LEN { return Err(SessionError::config(format!( "the session key is {} bytes, over the {MAX_SESSION_KEY_LEN} byte cookie limit", value.len() ))); } // 会话键会拼进 Redis key:只允许随机键字符与轮转时插入的 hash slot 花括号。 if !value .bytes() .all(|byte| byte.is_ascii_alphanumeric() || byte == b'{' || byte == b'}') { return Err(SessionError::config( "the session key contains characters outside [A-Za-z0-9{}]", )); } Ok(Self(value)) } } impl TryFrom<&str> for SessionKey { type Error = SessionError; fn try_from(value: &str) -> Result { Self::try_from(value.to_owned()) } } impl std::str::FromStr for SessionKey { type Err = SessionError; fn from_str(value: &str) -> Result { Self::try_from(value.to_owned()) } } impl AsRef for SessionKey { fn as_ref(&self) -> &str { &self.0 } } impl Display for SessionKey { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_str(&self.0) } } impl From for String { fn from(value: SessionKey) -> Self { value.0 } }