multi_tier_cache/
builder.rs

1//! Cache System Builder
2//!
3//! Provides a flexible builder pattern for constructing `CacheSystem` with custom backends.
4//!
5//! # Example: Using Default Backends
6//!
7//! ```rust,no_run
8//! use multi_tier_cache::CacheSystemBuilder;
9//!
10//! #[tokio::main]
11//! async fn main() -> anyhow::Result<()> {
12//!     let cache = CacheSystemBuilder::new()
13//!         .build()
14//!         .await?;
15//!     Ok(())
16//! }
17//! ```
18//!
19//! # Example: Custom L1 Backend
20//!
21//! ```rust,ignore
22//! use multi_tier_cache::{CacheSystemBuilder, CacheBackend};
23//! use std::sync::Arc;
24//!
25//! let custom_l1 = Arc::new(MyCustomL1Cache::new());
26//!
27//! let cache = CacheSystemBuilder::new()
28//!     .with_l1(custom_l1)
29//!     .build()
30//!     .await?;
31//! ```
32
33use crate::backends::MokaCacheConfig;
34use crate::traits::{CacheBackend, L2CacheBackend, StreamingBackend};
35use crate::{CacheManager, CacheSystem, CacheTier, L1Cache, L2Cache, TierConfig};
36use anyhow::Result;
37use std::sync::Arc;
38use tracing::info;
39
40/// Builder for constructing `CacheSystem` with custom backends
41///
42/// This builder allows you to configure custom L1 (in-memory) and L2 (distributed)
43/// cache backends, enabling you to swap Moka and Redis with alternative implementations.
44///
45/// # Multi-Tier Support (v0.5.0+)
46///
47/// The builder now supports dynamic multi-tier architectures (L1+L2+L3+L4+...).
48/// Use `.with_tier()` to add custom tiers, or `.with_l3()` / `.with_l4()` for convenience.
49///
50/// # Default Behavior
51///
52/// If no custom backends are provided, the builder uses:
53/// - **L1**: Moka in-memory cache
54/// - **L2**: Redis distributed cache
55///
56/// # Type Safety
57///
58/// The builder accepts any type that implements the required traits:
59/// - L1 backends must implement `CacheBackend`
60/// - L2 backends must implement `L2CacheBackend` (extends `CacheBackend`)
61/// - All tier backends must implement `L2CacheBackend` (for TTL support)
62/// - Streaming backends must implement `StreamingBackend`
63///
64/// # Example - Default 2-Tier
65///
66/// ```rust,no_run
67/// use multi_tier_cache::CacheSystemBuilder;
68///
69/// #[tokio::main]
70/// async fn main() -> anyhow::Result<()> {
71///     // Use default backends (Moka + Redis)
72///     let cache = CacheSystemBuilder::new()
73///         .build()
74///         .await?;
75///
76///     Ok(())
77/// }
78/// ```
79///
80/// # Example - Custom 3-Tier (v0.5.0+)
81///
82/// ```rust,ignore
83/// use multi_tier_cache::{CacheSystemBuilder, TierConfig};
84/// use std::sync::Arc;
85///
86/// let l1 = Arc::new(L1Cache::new().await?);
87/// let l2 = Arc::new(L2Cache::new().await?);
88/// let l3 = Arc::new(RocksDBCache::new("/tmp/cache").await?);
89///
90/// let cache = CacheSystemBuilder::new()
91///     .with_tier(l1, TierConfig::as_l1())
92///     .with_tier(l2, TierConfig::as_l2())
93///     .with_l3(l3)  // Convenience method
94///     .build()
95///     .await?;
96/// ```
97pub struct CacheSystemBuilder {
98    // Legacy 2-tier configuration (v0.1.0 - v0.4.x)
99    l1_backend: Option<Arc<dyn CacheBackend>>,
100    l2_backend: Option<Arc<dyn L2CacheBackend>>,
101
102    streaming_backend: Option<Arc<dyn StreamingBackend>>,
103    moka_config: Option<MokaCacheConfig>,
104
105    // Multi-tier configuration (v0.5.0+)
106    tiers: Vec<(Arc<dyn L2CacheBackend>, TierConfig)>,
107}
108
109impl CacheSystemBuilder {
110    /// Create a new builder with no custom backends configured
111    ///
112    /// By default, calling `.build()` will use Moka (L1) and Redis (L2).
113    /// Use `.with_tier()` to configure multi-tier architecture (v0.5.0+).
114    #[must_use]
115    pub fn new() -> Self {
116        Self {
117            l1_backend: None,
118            l2_backend: None,
119
120            streaming_backend: None,
121            moka_config: None,
122            tiers: Vec::new(),
123        }
124    }
125
126    /// Configure a custom L1 (in-memory) cache backend
127    ///
128    /// # Arguments
129    ///
130    /// * `backend` - Any type implementing `CacheBackend` trait
131    ///
132    /// # Example
133    ///
134    /// ```rust,ignore
135    /// use std::sync::Arc;
136    /// use multi_tier_cache::CacheSystemBuilder;
137    ///
138    /// let custom_l1 = Arc::new(MyCustomL1::new());
139    ///
140    /// let cache = CacheSystemBuilder::new()
141    ///     .with_l1(custom_l1)
142    ///     .build()
143    ///     .await?;
144    /// ```
145    #[must_use]
146    pub fn with_l1(mut self, backend: Arc<dyn CacheBackend>) -> Self {
147        self.l1_backend = Some(backend);
148        self
149    }
150
151    /// Configure custom configuration for default L1 (Moka) backend
152    #[must_use]
153    pub fn with_moka_config(mut self, config: MokaCacheConfig) -> Self {
154        self.moka_config = Some(config);
155        self
156    }
157
158    /// Configure a custom L2 (distributed) cache backend
159    ///
160    /// # Arguments
161    ///
162    /// * `backend` - Any type implementing `L2CacheBackend` trait
163    ///
164    /// # Example
165    ///
166    /// ```rust,ignore
167    /// use std::sync::Arc;
168    /// use multi_tier_cache::CacheSystemBuilder;
169    ///
170    /// let custom_l2 = Arc::new(MyMemcachedBackend::new());
171    ///
172    /// let cache = CacheSystemBuilder::new()
173    ///     .with_l2(custom_l2)
174    ///     .build()
175    ///     .await?;
176    /// ```
177    #[must_use]
178    pub fn with_l2(mut self, backend: Arc<dyn L2CacheBackend>) -> Self {
179        self.l2_backend = Some(backend);
180        self
181    }
182
183    /// Configure a custom streaming backend
184    ///
185    /// This is optional. If not provided, streaming functionality will use
186    /// the L2 backend if it implements `StreamingBackend`.
187    ///
188    /// # Arguments
189    ///
190    /// * `backend` - Any type implementing `StreamingBackend` trait
191    ///
192    /// # Example
193    ///
194    /// ```rust,ignore
195    /// use std::sync::Arc;
196    /// use multi_tier_cache::CacheSystemBuilder;
197    ///
198    /// let kafka_backend = Arc::new(MyKafkaBackend::new());
199    ///
200    /// let cache = CacheSystemBuilder::new()
201    ///     .with_streams(kafka_backend)
202    ///     .build()
203    ///     .await?;
204    /// ```
205    #[must_use]
206    pub fn with_streams(mut self, backend: Arc<dyn StreamingBackend>) -> Self {
207        self.streaming_backend = Some(backend);
208        self
209    }
210
211    /// Configure a cache tier with custom settings (v0.5.0+)
212    ///
213    /// Add a cache tier to the multi-tier architecture. Tiers will be sorted
214    /// by `tier_level` during build.
215    ///
216    /// # Arguments
217    ///
218    /// * `backend` - Any type implementing `L2CacheBackend` trait
219    /// * `config` - Tier configuration (level, promotion, TTL scale)
220    ///
221    /// # Example
222    ///
223    /// ```rust,ignore
224    /// use multi_tier_cache::{CacheSystemBuilder, TierConfig, L1Cache, L2Cache};
225    /// use std::sync::Arc;
226    ///
227    /// let l1 = Arc::new(L1Cache::new().await?);
228    /// let l2 = Arc::new(L2Cache::new().await?);
229    /// let l3 = Arc::new(RocksDBCache::new("/tmp").await?);
230    ///
231    /// let cache = CacheSystemBuilder::new()
232    ///     .with_tier(l1, TierConfig::as_l1())
233    ///     .with_tier(l2, TierConfig::as_l2())
234    ///     .with_tier(l3, TierConfig::as_l3())
235    ///     .build()
236    ///     .await?;
237    /// ```
238    #[must_use]
239    pub fn with_tier(mut self, backend: Arc<dyn L2CacheBackend>, config: TierConfig) -> Self {
240        self.tiers.push((backend, config));
241        self
242    }
243
244    /// Convenience method to add L3 cache tier (v0.5.0+)
245    ///
246    /// Adds a cold storage tier with 2x TTL multiplier.
247    ///
248    /// # Arguments
249    ///
250    /// * `backend` - L3 backend (e.g., `RocksDB`, `LevelDB`)
251    ///
252    /// # Example
253    ///
254    /// ```rust,ignore
255    /// use std::sync::Arc;
256    ///
257    /// let rocksdb = Arc::new(RocksDBCache::new("/tmp/l3cache").await?);
258    ///
259    /// let cache = CacheSystemBuilder::new()
260    ///     .with_l3(rocksdb)
261    ///     .build()
262    ///     .await?;
263    /// ```
264    #[must_use]
265    pub fn with_l3(mut self, backend: Arc<dyn L2CacheBackend>) -> Self {
266        self.tiers.push((backend, TierConfig::as_l3()));
267        self
268    }
269
270    /// Convenience method to add L4 cache tier (v0.5.0+)
271    ///
272    /// Adds an archive storage tier with 8x TTL multiplier.
273    ///
274    /// # Arguments
275    ///
276    /// * `backend` - L4 backend (e.g., S3, file system)
277    ///
278    /// # Example
279    ///
280    /// ```rust,ignore
281    /// use std::sync::Arc;
282    ///
283    /// let s3_cache = Arc::new(S3Cache::new("my-bucket").await?);
284    ///
285    /// let cache = CacheSystemBuilder::new()
286    ///     .with_l4(s3_cache)
287    ///     .build()
288    ///     .await?;
289    /// ```
290    #[must_use]
291    pub fn with_l4(mut self, backend: Arc<dyn L2CacheBackend>) -> Self {
292        self.tiers.push((backend, TierConfig::as_l4()));
293        self
294    }
295
296    /// Build the `CacheSystem` with configured or default backends
297    ///
298    /// If no custom backends were provided via `.with_l1()` or `.with_l2()`,
299    /// this method creates default backends (Moka for L1, Redis for L2).
300    ///
301    /// # Multi-Tier Mode (v0.5.0+)
302    ///
303    /// If tiers were configured via `.with_tier()`, `.with_l3()`, or `.with_l4()`,
304    /// the builder creates a multi-tier `CacheManager` using `new_with_tiers()`.
305    ///
306    /// # Returns
307    ///
308    /// * `Ok(CacheSystem)` - Successfully constructed cache system
309    /// * `Err(e)` - Failed to initialize backends (e.g., Redis connection error)
310    ///
311    /// # Example - Default 2-Tier
312    ///
313    /// ```rust,no_run
314    /// use multi_tier_cache::CacheSystemBuilder;
315    ///
316    /// #[tokio::main]
317    /// async fn main() -> anyhow::Result<()> {
318    ///     let cache = CacheSystemBuilder::new()
319    ///         .build()
320    ///         .await?;
321    ///
322    ///     // Use cache_manager for operations
323    ///     let manager = cache.cache_manager();
324    ///
325    ///     Ok(())
326    /// }
327    /// ```
328    /// # Errors
329    ///
330    /// Returns an error if the default backends cannot be initialized.
331    pub async fn build(self) -> Result<CacheSystem> {
332        info!("Building Multi-Tier Cache System");
333
334        // NEW: Multi-tier mode (v0.5.0+)
335        if !self.tiers.is_empty() {
336            info!(
337                tier_count = self.tiers.len(),
338                "Initializing multi-tier architecture"
339            );
340
341            // Sort tiers by tier_level (ascending: L1 first, L4 last)
342            let mut tiers = self.tiers;
343            tiers.sort_by_key(|(_, config)| config.tier_level);
344
345            // Convert to CacheTier instances
346            let cache_tiers: Vec<CacheTier> = tiers
347                .into_iter()
348                .map(|(backend, config)| {
349                    CacheTier::new(
350                        backend,
351                        config.tier_level,
352                        config.promotion_enabled,
353                        config.ttl_scale,
354                    )
355                })
356                .collect();
357
358            // Create cache manager with multi-tier support
359            let cache_manager = Arc::new(CacheManager::new_with_tiers(
360                cache_tiers,
361                self.streaming_backend,
362            )?);
363
364            info!("Multi-Tier Cache System built successfully");
365            info!("Note: Using multi-tier mode - use cache_manager() for all operations");
366
367            return Ok(CacheSystem {
368                cache_manager,
369                l1_cache: None, // Multi-tier mode doesn't use concrete types
370                l2_cache: None,
371            });
372        }
373
374        // LEGACY: 2-tier mode (v0.1.0 - v0.4.x)
375        // Handle default vs custom backends
376        if self.l1_backend.is_none() && self.l2_backend.is_none() {
377            // Default path: Create concrete types once and reuse them
378            info!("Initializing default backends (Moka + Redis)");
379
380            let l1_cache = Arc::new(L1Cache::new(self.moka_config.unwrap_or_default())?);
381            let l2_cache = Arc::new(L2Cache::new().await?);
382
383            // Use legacy constructor that handles conversion to trait objects
384            let cache_manager =
385                Arc::new(CacheManager::new(l1_cache.clone(), l2_cache.clone()).await?);
386
387            info!("Multi-Tier Cache System built successfully");
388
389            Ok(CacheSystem {
390                cache_manager,
391                l1_cache: Some(l1_cache),
392                l2_cache: Some(l2_cache),
393            })
394        } else {
395            // Custom backend path
396            info!("Building with custom backends");
397
398            let l1_backend: Arc<dyn CacheBackend> = if let Some(backend) = self.l1_backend {
399                info!(backend = %backend.name(), "Using custom L1 backend");
400                backend
401            } else {
402                info!("Using default L1 backend (Moka)");
403                let config = self.moka_config.unwrap_or_default();
404                Arc::new(L1Cache::new(config)?)
405            };
406
407            let l2_backend: Arc<dyn L2CacheBackend> = if let Some(backend) = self.l2_backend {
408                info!(backend = %backend.name(), "Using custom L2 backend");
409                backend
410            } else {
411                info!("Using default L2 backend (Redis)");
412                Arc::new(L2Cache::new().await?)
413            };
414
415            let streaming_backend = self.streaming_backend;
416
417            // Create cache manager with trait objects
418            let cache_manager = Arc::new(CacheManager::new_with_backends(
419                l1_backend,
420                l2_backend,
421                streaming_backend,
422            )?);
423
424            info!("Multi-Tier Cache System built with custom backends");
425            info!("Note: Using custom backends - use cache_manager() for all operations");
426
427            Ok(CacheSystem {
428                cache_manager,
429                l1_cache: None, // Custom backends mode doesn't use concrete types
430                l2_cache: None,
431            })
432        }
433    }
434}
435
436impl Default for CacheSystemBuilder {
437    fn default() -> Self {
438        Self::new()
439    }
440}