26af08e
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<String> for SessionKey {
type Error = SessionError;
fn try_from(value: String) -> Result<Self, Self::Error> {
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, Self::Error> {
Self::try_from(value.to_owned())
}
}
impl std::str::FromStr for SessionKey {
type Err = SessionError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
Self::try_from(value.to_owned())
}
}
impl AsRef<str> 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<SessionKey> for String {
fn from(value: SessionKey) -> Self {
value.0
}
}