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