use crate::core::TokenStore;
use crate::errors::JwtError;
use super::memory::InMemoryRefreshTokenStore;
#[derive(Debug, Clone, PartialEq)]
pub enum StoreType {
Memory,
Redis,
}
pub struct StoreConfig {
pub store_type: StoreType,
#[cfg(feature = "redis-store")]
pub redis: Option<super::redis::RedisConfig>,
}
impl Default for StoreConfig {
fn default() -> Self {
Self {
store_type: StoreType::Memory,
#[cfg(feature = "redis-store")]
redis: None,
}
}
}
pub struct Factory;
impl Factory {
pub fn new() -> Self {
Factory
}
pub async fn create_store(
&self,
config: &StoreConfig,
) -> Result<Box<dyn TokenStore>, JwtError> {
match config.store_type {
StoreType::Memory => Ok(Box::new(InMemoryRefreshTokenStore::new())),
StoreType::Redis => {
#[cfg(feature = "redis-store")]
{
let redis_config = config.redis.clone().unwrap_or_default();
let store = super::redis::RedisRefreshTokenStore::new(&redis_config).await?;
Ok(Box::new(store))
}
#[cfg(not(feature = "redis-store"))]
{
Err(JwtError::Internal(
"Redis store feature not enabled. Enable the 'redis-store' feature in Cargo.toml".into(),
))
}
}
}
}
}
impl Default for Factory {
fn default() -> Self {
Self::new()
}
}
pub fn new_memory_store() -> Box<dyn TokenStore> {
Box::new(InMemoryRefreshTokenStore::new())
}
pub fn must_new_memory_store() -> Box<dyn TokenStore> {
new_memory_store()
}
pub async fn new_store(config: &StoreConfig) -> Result<Box<dyn TokenStore>, JwtError> {
Factory::new().create_store(config).await
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_factory_create_store_memory() {
let factory = Factory::new();
let config = StoreConfig {
store_type: StoreType::Memory,
#[cfg(feature = "redis-store")]
redis: None,
};
let store = factory.create_store(&config).await;
assert!(
store.is_ok(),
"Factory should create memory store successfully"
);
let store = store.unwrap();
let count = store.count().await.unwrap();
assert_eq!(count, 0, "New memory store should be empty");
}
#[tokio::test]
async fn test_factory_default_config() {
let factory = Factory::new();
let config = StoreConfig::default();
assert_eq!(
config.store_type,
StoreType::Memory,
"Default config should use Memory type"
);
let store = factory.create_store(&config).await;
assert!(
store.is_ok(),
"Factory should create store from default config"
);
}
#[tokio::test]
async fn test_new_store_memory() {
let config = StoreConfig {
store_type: StoreType::Memory,
#[cfg(feature = "redis-store")]
redis: None,
};
let store = new_store(&config).await;
assert!(
store.is_ok(),
"new_store() should create memory store successfully"
);
let store = store.unwrap();
let count = store.count().await.unwrap();
assert_eq!(count, 0);
}
#[tokio::test]
async fn test_new_memory_store() {
let store = new_memory_store();
let count = store.count().await.unwrap();
assert_eq!(count, 0, "new_memory_store() should return an empty store");
}
#[tokio::test]
async fn test_default_store() {
let store = crate::store::default_store();
let count = store.count().await.unwrap();
assert_eq!(count, 0, "default_store() should return an empty store");
}
#[tokio::test]
async fn test_must_new_memory_store() {
let store = must_new_memory_store();
let count = store.count().await.unwrap();
assert_eq!(
count, 0,
"must_new_memory_store() should return an empty store"
);
}
#[cfg(not(feature = "redis-store"))]
#[tokio::test]
async fn test_factory_create_store_redis_without_feature() {
let factory = Factory::new();
let config = StoreConfig {
store_type: StoreType::Redis,
};
let result = factory.create_store(&config).await;
assert!(
result.is_err(),
"Creating Redis store without feature should fail"
);
let err_msg = match result {
Err(e) => e.to_string(),
Ok(_) => panic!("Expected error but got Ok"),
};
assert!(
err_msg.contains("Redis store feature not enabled"),
"Error should mention feature not enabled, got: {}",
err_msg
);
}
}