use std::{
collections::HashMap,
sync::{Mutex, PoisonError},
};
use crate::TokenSet;
pub trait TokenStore: Send + Sync {
fn get(&self, key: &str) -> Option<TokenSet>;
fn put(&self, key: &str, tokens: &TokenSet);
fn remove(&self, key: &str);
}
#[derive(Debug, Default)]
pub struct InMemoryTokenStore {
entries: Mutex<HashMap<String, TokenSet>>,
}
impl InMemoryTokenStore {
#[inline]
pub fn new() -> Self {
Self::default()
}
}
impl TokenStore for InMemoryTokenStore {
fn get(&self, key: &str) -> Option<TokenSet> {
self.entries
.lock()
.unwrap_or_else(PoisonError::into_inner)
.get(key)
.cloned()
}
fn put(&self, key: &str, tokens: &TokenSet) {
self.entries
.lock()
.unwrap_or_else(PoisonError::into_inner)
.insert(key.to_owned(), tokens.clone());
}
fn remove(&self, key: &str) {
self.entries
.lock()
.unwrap_or_else(PoisonError::into_inner)
.remove(key);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn tokens(access_token: &str) -> TokenSet {
TokenSet {
access_token: access_token.into(),
token_type: "Bearer".into(),
refresh_token: None,
scope: None,
id_token: None,
expires_at: None,
}
}
#[test]
fn it_stores_replaces_and_removes_entries() {
let store = InMemoryTokenStore::new();
assert!(store.get("alice").is_none());
store.put("alice", &tokens("a1"));
store.put("bob", &tokens("b1"));
assert_eq!(store.get("alice").unwrap().access_token, "a1");
store.put("alice", &tokens("a2"));
assert_eq!(store.get("alice").unwrap().access_token, "a2");
store.remove("alice");
assert!(store.get("alice").is_none());
assert!(store.get("bob").is_some());
}
}