use std::{collections::HashMap, sync::Arc};
use parking_lot::RwLock;
use super::id::ClientId;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SessionToken(Arc<str>);
impl SessionToken {
#[must_use]
pub fn generate() -> Self {
Self(format!("{:032x}", rand::random::<u128>()).into())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for SessionToken {
fn from(s: &str) -> Self {
Self(s.into())
}
}
impl std::fmt::Display for SessionToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
struct TokenMaps {
forward: HashMap<SessionToken, ClientId>,
reverse: HashMap<ClientId, SessionToken>,
}
pub struct TokenRegistry {
maps: RwLock<TokenMaps>,
}
impl TokenRegistry {
#[must_use]
pub fn new() -> Self {
Self {
maps: RwLock::new(TokenMaps {
forward: HashMap::new(),
reverse: HashMap::new(),
}),
}
}
pub fn register(&self, client_id: ClientId) -> SessionToken {
let token = SessionToken::generate();
let mut maps = self.maps.write();
if let Some(old_token) = maps.reverse.remove(&client_id) {
maps.forward.remove(&old_token);
}
maps.forward.insert(token.clone(), client_id);
maps.reverse.insert(client_id, token.clone());
token
}
#[must_use]
pub fn resolve(&self, token: &SessionToken) -> Option<ClientId> {
self.maps.read().forward.get(token).copied()
}
pub fn revoke(&self, token: &SessionToken) -> Option<ClientId> {
let mut maps = self.maps.write();
let client_id = maps.forward.remove(token)?;
maps.reverse.remove(&client_id);
drop(maps);
Some(client_id)
}
pub fn revoke_by_client(&self, client_id: ClientId) -> Option<SessionToken> {
let mut maps = self.maps.write();
let token = maps.reverse.remove(&client_id)?;
maps.forward.remove(&token);
drop(maps);
Some(token)
}
#[must_use]
pub fn len(&self) -> usize {
self.maps.read().forward.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.maps.read().forward.is_empty()
}
}
impl Default for TokenRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[path = "token_registry_tests.rs"]
mod tests;