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 std::sync::Arc;
34use anyhow::Result;
35use crate::traits::{CacheBackend, L2CacheBackend, StreamingBackend};
36use crate::{CacheManager, L1Cache, L2Cache, CacheSystem};
37
38/// Builder for constructing CacheSystem with custom backends
39///
40/// This builder allows you to configure custom L1 (in-memory) and L2 (distributed)
41/// cache backends, enabling you to swap Moka and Redis with alternative implementations.
42///
43/// # Default Behavior
44///
45/// If no custom backends are provided, the builder uses:
46/// - **L1**: Moka in-memory cache
47/// - **L2**: Redis distributed cache
48///
49/// # Type Safety
50///
51/// The builder accepts any type that implements the required traits:
52/// - L1 backends must implement `CacheBackend`
53/// - L2 backends must implement `L2CacheBackend` (extends `CacheBackend`)
54/// - Streaming backends must implement `StreamingBackend`
55///
56/// # Example
57///
58/// ```rust,no_run
59/// use multi_tier_cache::CacheSystemBuilder;
60///
61/// #[tokio::main]
62/// async fn main() -> anyhow::Result<()> {
63///     // Use default backends (Moka + Redis)
64///     let cache = CacheSystemBuilder::new()
65///         .build()
66///         .await?;
67///
68///     Ok(())
69/// }
70/// ```
71pub struct CacheSystemBuilder {
72    l1_backend: Option<Arc<dyn CacheBackend>>,
73    l2_backend: Option<Arc<dyn L2CacheBackend>>,
74    streaming_backend: Option<Arc<dyn StreamingBackend>>,
75}
76
77impl CacheSystemBuilder {
78    /// Create a new builder with no custom backends configured
79    ///
80    /// By default, calling `.build()` will use Moka (L1) and Redis (L2).
81    pub fn new() -> Self {
82        Self {
83            l1_backend: None,
84            l2_backend: None,
85            streaming_backend: None,
86        }
87    }
88
89    /// Configure a custom L1 (in-memory) cache backend
90    ///
91    /// # Arguments
92    ///
93    /// * `backend` - Any type implementing `CacheBackend` trait
94    ///
95    /// # Example
96    ///
97    /// ```rust,ignore
98    /// use std::sync::Arc;
99    /// use multi_tier_cache::CacheSystemBuilder;
100    ///
101    /// let custom_l1 = Arc::new(MyCustomL1::new());
102    ///
103    /// let cache = CacheSystemBuilder::new()
104    ///     .with_l1(custom_l1)
105    ///     .build()
106    ///     .await?;
107    /// ```
108    pub fn with_l1(mut self, backend: Arc<dyn CacheBackend>) -> Self {
109        self.l1_backend = Some(backend);
110        self
111    }
112
113    /// Configure a custom L2 (distributed) cache backend
114    ///
115    /// # Arguments
116    ///
117    /// * `backend` - Any type implementing `L2CacheBackend` trait
118    ///
119    /// # Example
120    ///
121    /// ```rust,ignore
122    /// use std::sync::Arc;
123    /// use multi_tier_cache::CacheSystemBuilder;
124    ///
125    /// let custom_l2 = Arc::new(MyMemcachedBackend::new());
126    ///
127    /// let cache = CacheSystemBuilder::new()
128    ///     .with_l2(custom_l2)
129    ///     .build()
130    ///     .await?;
131    /// ```
132    pub fn with_l2(mut self, backend: Arc<dyn L2CacheBackend>) -> Self {
133        self.l2_backend = Some(backend);
134        self
135    }
136
137    /// Configure a custom streaming backend
138    ///
139    /// This is optional. If not provided, streaming functionality will use
140    /// the L2 backend if it implements `StreamingBackend`.
141    ///
142    /// # Arguments
143    ///
144    /// * `backend` - Any type implementing `StreamingBackend` trait
145    ///
146    /// # Example
147    ///
148    /// ```rust,ignore
149    /// use std::sync::Arc;
150    /// use multi_tier_cache::CacheSystemBuilder;
151    ///
152    /// let kafka_backend = Arc::new(MyKafkaBackend::new());
153    ///
154    /// let cache = CacheSystemBuilder::new()
155    ///     .with_streams(kafka_backend)
156    ///     .build()
157    ///     .await?;
158    /// ```
159    pub fn with_streams(mut self, backend: Arc<dyn StreamingBackend>) -> Self {
160        self.streaming_backend = Some(backend);
161        self
162    }
163
164    /// Build the CacheSystem with configured or default backends
165    ///
166    /// If no custom backends were provided via `.with_l1()` or `.with_l2()`,
167    /// this method creates default backends (Moka for L1, Redis for L2).
168    ///
169    /// # Returns
170    ///
171    /// * `Ok(CacheSystem)` - Successfully constructed cache system
172    /// * `Err(e)` - Failed to initialize backends (e.g., Redis connection error)
173    ///
174    /// # Note
175    ///
176    /// Currently, custom backends are stored but not yet used by CacheManager.
177    /// Full trait-based backend support will be available in the next phase.
178    /// For now, this builder ensures API stability for future upgrades.
179    ///
180    /// # Example
181    ///
182    /// ```rust,no_run
183    /// use multi_tier_cache::CacheSystemBuilder;
184    ///
185    /// #[tokio::main]
186    /// async fn main() -> anyhow::Result<()> {
187    ///     let cache = CacheSystemBuilder::new()
188    ///         .build()
189    ///         .await?;
190    ///
191    ///     // Use cache_manager for operations
192    ///     let manager = cache.cache_manager();
193    ///
194    ///     Ok(())
195    /// }
196    /// ```
197    pub async fn build(self) -> Result<CacheSystem> {
198        println!("đŸ—ī¸ Building Multi-Tier Cache System...");
199
200        // Handle default vs custom backends
201        if self.l1_backend.is_none() && self.l2_backend.is_none() {
202            // Default path: Create concrete types once and reuse them
203            println!("  🚀 Initializing default backends (Moka + Redis)...");
204
205            let l1_cache = Arc::new(L1Cache::new().await?);
206            let l2_cache = Arc::new(L2Cache::new().await?);
207
208            // Use legacy constructor that handles conversion to trait objects
209            let cache_manager = Arc::new(CacheManager::new(l1_cache.clone(), l2_cache.clone()).await?);
210
211            println!("✅ Multi-Tier Cache System built successfully");
212
213            Ok(CacheSystem {
214                cache_manager,
215                l1_cache,
216                l2_cache,
217            })
218        } else {
219            // Custom backend path
220            println!("  â„šī¸ Building with custom backends...");
221
222            let l1_backend: Arc<dyn CacheBackend> = match self.l1_backend {
223                Some(backend) => {
224                    println!("  ✅ Using custom L1 backend: {}", backend.name());
225                    backend
226                }
227                None => {
228                    println!("  🚀 Using default L1 backend (Moka)");
229                    Arc::new(L1Cache::new().await?)
230                }
231            };
232
233            let l2_backend: Arc<dyn L2CacheBackend> = match self.l2_backend {
234                Some(backend) => {
235                    println!("  ✅ Using custom L2 backend: {}", backend.name());
236                    backend
237                }
238                None => {
239                    println!("  🔴 Using default L2 backend (Redis)");
240                    Arc::new(L2Cache::new().await?)
241                }
242            };
243
244            let streaming_backend = self.streaming_backend;
245
246            // Create cache manager with trait objects
247            let cache_manager = Arc::new(
248                CacheManager::new_with_backends(
249                    l1_backend,
250                    l2_backend,
251                    streaming_backend,
252                ).await?
253            );
254
255            // Create placeholder concrete types for backward compatibility
256            // Note: These are NOT the same instances as used by cache_manager
257            // Users must use cache_manager() for all operations
258            let l1_placeholder = Arc::new(L1Cache::new().await?);
259            let l2_placeholder = Arc::new(L2Cache::new().await?);
260
261            println!("✅ Multi-Tier Cache System built with custom backends");
262            println!("  âš ī¸ Note: Direct l1_cache/l2_cache fields are placeholders");
263            println!("  âš ī¸ Use cache_manager() for all cache operations");
264
265            Ok(CacheSystem {
266                cache_manager,
267                l1_cache: l1_placeholder,
268                l2_cache: l2_placeholder,
269            })
270        }
271    }
272}
273
274impl Default for CacheSystemBuilder {
275    fn default() -> Self {
276        Self::new()
277    }
278}