mod core;
mod manager;
mod backends;
mod config;
pub use config::{RiCacheConfig, RiCacheBackendType, RiCachePolicy};
pub use manager::{RiCacheManager, RiCacheEvent};
pub use core::{RiCachedValue, RiCacheStats, RiCache, RiCacheEvent as CoreCacheEvent};
pub use backends::RiMemoryCache;
#[cfg(feature = "redis")]
pub use backends::{RiRedisCache, RiHybridCache};
use crate::core::{RiResult, RiServiceContext};
use std::sync::Arc;
use tokio::sync::RwLock;
#[cfg(feature = "pyo3")]
use pyo3::pymethods;
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct RiCacheModule {
config: RiCacheConfig,
manager: std::sync::Arc<tokio::sync::RwLock<RiCacheManager>>,
}
impl RiCacheModule {
pub async fn new(config: RiCacheConfig) -> Self {
#[cfg(feature = "redis")]
let backend: std::sync::Arc<dyn crate::cache::RiCache> = match config.backend_type {
crate::cache::config::RiCacheBackendType::Memory => {
std::sync::Arc::new(RiMemoryCache::new())
}
crate::cache::config::RiCacheBackendType::Redis => {
match RiRedisCache::new(&config.redis_url).await {
Ok(cache) => Arc::new(cache),
Err(e) => {
log::warn!("Failed to create Redis cache ({}): {}. Falling back to memory backend", config.redis_url, e);
Arc::new(RiMemoryCache::new()) as Arc<dyn crate::cache::RiCache>
}
}
}
crate::cache::config::RiCacheBackendType::Hybrid => {
match RiHybridCache::new(&config.redis_url).await {
Ok(backend) => Arc::new(backend),
Err(e) => {
log::warn!("Failed to create hybrid cache backend (Redis URL: {}): {}. Falling back to memory backend", config.redis_url, e);
Arc::new(RiMemoryCache::new()) as Arc<dyn crate::cache::RiCache>
}
}
}
};
#[cfg(not(feature = "redis"))]
let backend: std::sync::Arc<dyn crate::cache::RiCache> = match config.backend_type {
crate::cache::config::RiCacheBackendType::Memory => {
std::sync::Arc::new(RiMemoryCache::new())
}
crate::cache::config::RiCacheBackendType::Redis | crate::cache::config::RiCacheBackendType::Hybrid => {
std::sync::Arc::new(RiMemoryCache::new())
}
};
let manager = RiCacheManager::new(backend);
Self {
config,
manager: Arc::new(RwLock::new(manager)),
}
}
pub fn with_config(config: RiCacheConfig) -> Self {
#[cfg(feature = "redis")]
let backend: std::sync::Arc<dyn crate::cache::RiCache> = match config.backend_type {
crate::cache::config::RiCacheBackendType::Memory => {
std::sync::Arc::new(RiMemoryCache::new())
}
crate::cache::config::RiCacheBackendType::Redis => {
match tokio::runtime::Handle::try_current() {
Ok(handle) => {
match handle.block_on(RiRedisCache::new(&config.redis_url)) {
Ok(cache) => Arc::new(cache),
Err(e) => {
log::warn!("Failed to create Redis cache ({}): {}. Falling back to memory backend", config.redis_url, e);
Arc::new(RiMemoryCache::new()) as Arc<dyn crate::cache::RiCache>
}
}
}
Err(_) => {
log::warn!("No Tokio runtime available for Redis cache creation. Falling back to memory backend");
Arc::new(RiMemoryCache::new()) as Arc<dyn crate::cache::RiCache>
}
}
}
crate::cache::config::RiCacheBackendType::Hybrid => {
match tokio::runtime::Handle::try_current() {
Ok(handle) => {
match handle.block_on(RiHybridCache::new(&config.redis_url)) {
Ok(backend) => Arc::new(backend),
Err(e) => {
log::warn!("Failed to create hybrid cache backend (Redis URL: {}): {}. Falling back to memory backend", config.redis_url, e);
Arc::new(RiMemoryCache::new()) as Arc<dyn crate::cache::RiCache>
}
}
}
Err(_) => {
log::warn!("No Tokio runtime available for hybrid cache creation. Falling back to memory backend");
Arc::new(RiMemoryCache::new()) as Arc<dyn crate::cache::RiCache>
}
}
}
};
#[cfg(not(feature = "redis"))]
let backend: std::sync::Arc<dyn crate::cache::RiCache> = match config.backend_type {
crate::cache::config::RiCacheBackendType::Memory => {
std::sync::Arc::new(RiMemoryCache::new())
}
crate::cache::config::RiCacheBackendType::Redis | crate::cache::config::RiCacheBackendType::Hybrid => {
log::warn!("Redis feature not enabled. Falling back to memory backend");
std::sync::Arc::new(RiMemoryCache::new())
}
};
let manager = RiCacheManager::new(backend);
Self {
config,
manager: Arc::new(RwLock::new(manager)),
}
}
pub fn cache_manager(&self) -> Arc<RwLock<RiCacheManager>> {
self.manager.clone()
}
}
#[cfg(feature = "pyo3")]
#[pymethods]
impl RiCacheModule {
#[new]
fn py_new(config: RiCacheConfig) -> Result<Self, pyo3::PyErr> {
let rt = match tokio::runtime::Runtime::new() {
Ok(r) => r,
Err(e) => {
return Err(pyo3::PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
format!("Failed to create runtime: {}", e),
));
}
};
rt.block_on(async {
Ok(Self::new(config).await)
})
}
fn get_cache_manager_status(&self) -> String {
format!("Cache manager initialized with backend: {:?}", self.config.backend_type)
}
}
#[async_trait::async_trait]
impl crate::core::RiModule for RiCacheModule {
fn name(&self) -> &str {
"Ri.Cache"
}
fn is_critical(&self) -> bool {
false }
async fn init(&mut self, ctx: &mut RiServiceContext) -> RiResult<()> {
log::info!("Initializing Ri Cache Module");
let binding = ctx.config();
let cfg = binding.config();
if let Some(cache_config) = cfg.get("cache") {
self.config = serde_yaml::from_str(cache_config)
.unwrap_or_else(|_| RiCacheConfig::default());
} else {
self.config = RiCacheConfig::default();
}
match self.config.backend_type {
crate::cache::config::RiCacheBackendType::Memory => {
let backend = Arc::new(RiMemoryCache::new());
let manager = RiCacheManager::new(backend);
*self.manager.write().await = manager;
}
#[cfg(feature = "redis")]
crate::cache::config::RiCacheBackendType::Redis => {
let backend = Arc::new(RiRedisCache::new(&self.config.redis_url).await?);
let manager = RiCacheManager::new(backend);
*self.manager.write().await = manager;
}
#[cfg(feature = "redis")]
crate::cache::config::RiCacheBackendType::Hybrid => {
let backend = Arc::new(RiHybridCache::new(&self.config.redis_url).await?);
let manager = RiCacheManager::new(backend);
*self.manager.write().await = manager;
}
#[cfg(not(feature = "redis"))]
crate::cache::config::RiCacheBackendType::Redis | crate::cache::config::RiCacheBackendType::Hybrid => {
let backend = Arc::new(RiMemoryCache::new());
let manager = RiCacheManager::new(backend);
*self.manager.write().await = manager;
}
}
if let Ok(fs) = crate::fs::RiFileSystem::new_auto_root() {
let logger = crate::log::RiLogger::new(&crate::log::RiLogConfig::default(), fs);
let _ = logger.info("cache", "Ri Cache Module initialized successfully");
}
Ok(())
}
async fn after_shutdown(&mut self, _ctx: &mut RiServiceContext) -> RiResult<()> {
log::info!("Cleaning up Ri Cache Module");
let manager = self.manager.read().await;
let stats = manager.stats().await;
log::info!("Cache stats: {stats:?}");
let cleaned = manager.cleanup_expired().await?;
log::info!("Cleaned up {cleaned} expired cache entries");
log::info!("Ri Cache Module cleanup completed");
Ok(())
}
}
impl crate::core::ServiceModule for RiCacheModule {
fn name(&self) -> &str {
"Ri.Cache"
}
fn is_critical(&self) -> bool {
false
}
fn priority(&self) -> i32 {
10
}
fn dependencies(&self) -> Vec<&str> {
vec![]
}
fn init(&mut self, _ctx: &mut crate::core::RiServiceContext) -> crate::core::RiResult<()> {
Ok(())
}
fn start(&mut self, _ctx: &mut crate::core::RiServiceContext) -> crate::core::RiResult<()> {
Ok(())
}
fn shutdown(&mut self, _ctx: &mut crate::core::RiServiceContext) -> crate::core::RiResult<()> {
Ok(())
}
}