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}