#[cfg(feature = "moka")]
#[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
use crate::backends::MokaCacheConfig;
use crate::traits::{CacheBackend, L2CacheBackend, StreamingBackend};
use crate::{CacheManager, CacheSystem, CacheTier, TierConfig};
#[cfg(feature = "moka")]
#[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
use crate::L1Cache;
#[cfg(feature = "redis")]
#[cfg_attr(docsrs, doc(cfg(feature = "redis")))]
use crate::L2Cache;
use anyhow::Result;
use std::sync::Arc;
use tracing::info;
pub struct CacheSystemBuilder {
l1_backend: Option<Arc<dyn CacheBackend>>,
l2_backend: Option<Arc<dyn L2CacheBackend>>,
streaming_backend: Option<Arc<dyn StreamingBackend>>,
#[cfg(feature = "moka")]
#[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
moka_config: Option<MokaCacheConfig>,
tiers: Vec<(Arc<dyn L2CacheBackend>, TierConfig)>,
}
impl CacheSystemBuilder {
#[must_use]
pub fn new() -> Self {
Self {
l1_backend: None,
l2_backend: None,
streaming_backend: None,
#[cfg(feature = "moka")]
#[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
moka_config: None,
tiers: Vec::new(),
}
}
#[must_use]
pub fn with_l1(mut self, backend: Arc<dyn CacheBackend>) -> Self {
self.l1_backend = Some(backend);
self
}
#[must_use]
#[cfg(feature = "moka")]
#[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
pub fn with_moka_config(mut self, config: MokaCacheConfig) -> Self {
self.moka_config = Some(config);
self
}
#[must_use]
pub fn with_l2(mut self, backend: Arc<dyn L2CacheBackend>) -> Self {
self.l2_backend = Some(backend);
self
}
#[must_use]
pub fn with_streams(mut self, backend: Arc<dyn StreamingBackend>) -> Self {
self.streaming_backend = Some(backend);
self
}
#[must_use]
pub fn with_tier(mut self, backend: Arc<dyn L2CacheBackend>, config: TierConfig) -> Self {
self.tiers.push((backend, config));
self
}
#[must_use]
pub fn with_l3(mut self, backend: Arc<dyn L2CacheBackend>) -> Self {
self.tiers.push((backend, TierConfig::as_l3()));
self
}
#[must_use]
pub fn with_l4(mut self, backend: Arc<dyn L2CacheBackend>) -> Self {
self.tiers.push((backend, TierConfig::as_l4()));
self
}
pub async fn build(self) -> Result<CacheSystem> {
info!("Building Multi-Tier Cache System");
if !self.tiers.is_empty() {
self.build_multi_tier()
} else if self.l1_backend.is_none() && self.l2_backend.is_none() {
self.build_default_2_tier().await
} else {
self.build_custom_2_tier().await
}
}
fn build_multi_tier(self) -> Result<CacheSystem> {
info!(
tier_count = self.tiers.len(),
"Initializing multi-tier architecture"
);
let mut tiers = self.tiers;
tiers.sort_by_key(|(_, config)| config.tier_level);
let cache_tiers: Vec<CacheTier> = tiers
.into_iter()
.map(|(backend, config)| {
CacheTier::new(
backend,
config.tier_level,
config.promotion_enabled,
config.promotion_frequency,
config.ttl_scale,
)
})
.collect();
let cache_manager = Arc::new(CacheManager::new_with_tiers(
cache_tiers,
self.streaming_backend,
)?);
info!("Multi-Tier Cache System built successfully");
info!("Note: Using multi-tier mode - use cache_manager() for all operations");
Ok(CacheSystem {
cache_manager,
#[cfg(feature = "moka")]
#[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
l1_cache: None,
#[cfg(feature = "redis")]
#[cfg_attr(docsrs, doc(cfg(feature = "redis")))]
l2_cache: None,
})
}
async fn build_default_2_tier(self) -> Result<CacheSystem> {
info!("Initializing default backends (Moka + Redis)");
#[cfg(all(feature = "moka", feature = "redis"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "moka", feature = "redis"))))]
{
let l1_cache = Arc::new(crate::L1Cache::new(self.moka_config.unwrap_or_default())?);
let l2_cache: Arc<crate::L2Cache> = Arc::new(crate::L2Cache::new().await?);
let cache_manager = Arc::new(CacheManager::new(l1_cache.clone(), l2_cache.clone()).await?);
info!("Multi-Tier Cache System built successfully");
Ok(CacheSystem {
cache_manager,
#[cfg(feature = "moka")]
#[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
l1_cache: Some(l1_cache),
#[cfg(feature = "redis")]
#[cfg_attr(docsrs, doc(cfg(feature = "redis")))]
l2_cache: Some(l2_cache),
})
}
#[cfg(not(all(feature = "moka", feature = "redis")))]
{
Err(anyhow::anyhow!(
"Default backends (Moka/Redis) are not enabled. Provide custom backends or enable 'moka' and 'redis' features."
))
}
}
async fn build_custom_2_tier(self) -> Result<CacheSystem> {
info!("Building with custom backends");
let l1_backend: Arc<dyn CacheBackend> = if let Some(backend) = self.l1_backend {
backend
} else {
#[cfg(feature = "moka")]
#[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
{
Arc::new(L1Cache::new(self.moka_config.unwrap_or_default())?)
}
#[cfg(not(feature = "moka"))]
{
return Err(anyhow::anyhow!(
"Moka feature not enabled. Provide a custom L1 backend."
));
}
};
let l2_backend: Arc<dyn L2CacheBackend> = if let Some(backend) = self.l2_backend {
backend
} else {
#[cfg(feature = "redis")]
#[cfg_attr(docsrs, doc(cfg(feature = "redis")))]
{
Arc::new(L2Cache::new().await?)
}
#[cfg(not(feature = "redis"))]
{
return Err(anyhow::anyhow!(
"Redis feature not enabled. Provide a custom L2 backend."
));
}
};
let streaming_backend = self.streaming_backend;
let cache_manager = Arc::new(CacheManager::new_with_backends(
l1_backend,
l2_backend,
streaming_backend,
)?);
info!("Multi-Tier Cache System built with custom backends");
info!("Note: Using custom backends - use cache_manager() for all operations");
Ok(CacheSystem {
cache_manager,
#[cfg(feature = "moka")]
#[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
l1_cache: None,
#[cfg(feature = "redis")]
#[cfg_attr(docsrs, doc(cfg(feature = "redis")))]
l2_cache: None,
})
}
}
impl Default for CacheSystemBuilder {
fn default() -> Self {
Self::new()
}
}