elif_orm/relationships/
loader.rs1use async_trait::async_trait;
4use sqlx::{Pool, Postgres};
5use std::collections::HashMap;
6use std::sync::Arc;
7use tokio::sync::RwLock;
8
9use crate::error::{ModelError, ModelResult};
10use crate::model::Model;
11
12#[async_trait]
14pub trait RelationshipLoader<T>: Send + Sync {
15 async fn load(&self, pool: &Pool<Postgres>) -> ModelResult<T>;
17
18 async fn reload(&self, pool: &Pool<Postgres>) -> ModelResult<T>;
20}
21
22pub struct CachedRelationshipLoader<T> {
24 loader_fn: Arc<
26 dyn Fn(&Pool<Postgres>) -> Pin<Box<dyn Future<Output = ModelResult<T>> + Send>>
27 + Send
28 + Sync,
29 >,
30 cache: Arc<RwLock<Option<T>>>,
32 loaded: Arc<RwLock<bool>>,
34}
35
36use std::future::Future;
37use std::pin::Pin;
38
39impl<T> CachedRelationshipLoader<T>
40where
41 T: Send + Sync,
42{
43 pub fn new<F, Fut>(loader: F) -> Self
45 where
46 F: Fn(&Pool<Postgres>) -> Fut + Send + Sync + 'static,
47 Fut: Future<Output = ModelResult<T>> + Send + 'static,
48 {
49 Self {
50 loader_fn: Arc::new(move |pool| Box::pin(loader(pool))),
51 cache: Arc::new(RwLock::new(None)),
52 loaded: Arc::new(RwLock::new(false)),
53 }
54 }
55}
56
57#[async_trait]
58impl<T> RelationshipLoader<T> for CachedRelationshipLoader<T>
59where
60 T: Send + Sync + Clone,
61{
62 async fn load(&self, pool: &Pool<Postgres>) -> ModelResult<T> {
63 {
65 let loaded = self.loaded.read().await;
66 if *loaded {
67 let cache = self.cache.read().await;
68 if let Some(ref value) = *cache {
69 return Ok(value.clone());
70 }
71 }
72 }
73
74 let result = (self.loader_fn)(pool).await?;
76
77 {
79 let mut cache = self.cache.write().await;
80 *cache = Some(result.clone());
81 }
82 {
83 let mut loaded = self.loaded.write().await;
84 *loaded = true;
85 }
86
87 Ok(result)
88 }
89
90 async fn reload(&self, pool: &Pool<Postgres>) -> ModelResult<T> {
91 {
93 let mut cache = self.cache.write().await;
94 *cache = None;
95 }
96 {
97 let mut loaded = self.loaded.write().await;
98 *loaded = false;
99 }
100
101 self.load(pool).await
102 }
103}
104
105#[derive(Debug, Clone)]
107pub struct AccessPattern {
108 pub access_count: usize,
110 pub should_auto_load: bool,
112 pub last_accessed: std::time::Instant,
114}
115
116impl Default for AccessPattern {
117 fn default() -> Self {
118 Self {
119 access_count: 0,
120 should_auto_load: false,
121 last_accessed: std::time::Instant::now(),
122 }
123 }
124}
125
126pub struct Lazy<T> {
128 loader: Box<dyn RelationshipLoader<T>>,
129 loaded: bool,
130 value: Option<T>,
131 access_pattern: Arc<RwLock<AccessPattern>>,
133}
134
135impl<T> Lazy<T>
136where
137 T: Clone + Send + Sync + 'static,
138{
139 pub fn new<L>(loader: L) -> Self
141 where
142 L: RelationshipLoader<T> + 'static,
143 {
144 Self {
145 loader: Box::new(loader),
146 loaded: false,
147 value: None,
148 access_pattern: Arc::new(RwLock::new(AccessPattern::default())),
149 }
150 }
151
152 pub fn loaded(value: T) -> Self {
154 Self {
155 loader: Box::new(NoOpLoader::new(value.clone())),
156 loaded: true,
157 value: Some(value),
158 access_pattern: Arc::new(RwLock::new(AccessPattern::default())),
159 }
160 }
161}
162
163impl<T> Lazy<T>
164where
165 T: Send + Sync,
166{
167 pub async fn get(&mut self, pool: &Pool<Postgres>) -> ModelResult<&T> {
169 {
171 let mut pattern = self.access_pattern.write().await;
172 pattern.access_count += 1;
173 pattern.last_accessed = std::time::Instant::now();
174
175 if pattern.access_count >= 3 {
177 pattern.should_auto_load = true;
178 }
179 }
180
181 if !self.loaded {
182 self.load(pool).await?;
183 }
184
185 self.value.as_ref().ok_or_else(|| {
186 ModelError::Database("Lazy relationship value not available".to_string())
187 })
188 }
189
190 pub async fn load(&mut self, pool: &Pool<Postgres>) -> ModelResult<&T> {
192 let value = self.loader.load(pool).await?;
193 self.value = Some(value);
194 self.loaded = true;
195
196 self.value.as_ref().ok_or_else(|| {
197 ModelError::Database("Failed to store lazy relationship value".to_string())
198 })
199 }
200
201 pub async fn reload(&mut self, pool: &Pool<Postgres>) -> ModelResult<&T> {
203 let value = self.loader.reload(pool).await?;
204 self.value = Some(value);
205 self.loaded = true;
206
207 self.value.as_ref().ok_or_else(|| {
208 ModelError::Database("Failed to store reloaded relationship value".to_string())
209 })
210 }
211}
212
213impl<T> Lazy<T> {
214 pub fn is_loaded(&self) -> bool {
216 self.loaded
217 }
218
219 pub fn take(&mut self) -> Option<T> {
221 self.loaded = false;
222 self.value.take()
223 }
224
225 pub fn set(&mut self, value: T) {
227 self.value = Some(value);
228 self.loaded = true;
229 }
230
231 pub fn clear(&mut self) {
233 self.value = None;
234 self.loaded = false;
235 }
236
237 pub async fn get_access_pattern(&self) -> AccessPattern {
239 self.access_pattern.read().await.clone()
240 }
241
242 pub async fn should_auto_load(&self) -> bool {
244 self.access_pattern.read().await.should_auto_load
245 }
246
247 pub async fn enable_auto_load(&self) {
249 let mut pattern = self.access_pattern.write().await;
250 pattern.should_auto_load = true;
251 }
252
253 pub async fn disable_auto_load(&self) {
255 let mut pattern = self.access_pattern.write().await;
256 pattern.should_auto_load = false;
257 }
258}
259
260struct NoOpLoader<T> {
262 value: T,
263}
264
265impl<T> NoOpLoader<T> {
266 fn new(value: T) -> Self {
267 Self { value }
268 }
269}
270
271#[async_trait]
272impl<T> RelationshipLoader<T> for NoOpLoader<T>
273where
274 T: Send + Sync + Clone,
275{
276 async fn load(&self, _pool: &Pool<Postgres>) -> ModelResult<T> {
277 Ok(self.value.clone())
278 }
279
280 async fn reload(&self, _pool: &Pool<Postgres>) -> ModelResult<T> {
281 Ok(self.value.clone())
282 }
283}
284
285pub struct RelationshipCache {
287 cache: Arc<RwLock<HashMap<String, HashMap<String, HashMap<String, serde_json::Value>>>>>,
289}
290
291impl RelationshipCache {
292 pub fn new() -> Self {
294 Self {
295 cache: Arc::new(RwLock::new(HashMap::new())),
296 }
297 }
298
299 pub async fn store(
301 &self,
302 model_type: &str,
303 model_id: &str,
304 relation: &str,
305 data: serde_json::Value,
306 ) {
307 let mut cache = self.cache.write().await;
308
309 cache
310 .entry(model_type.to_string())
311 .or_insert_with(HashMap::new)
312 .entry(model_id.to_string())
313 .or_insert_with(HashMap::new)
314 .insert(relation.to_string(), data);
315 }
316
317 pub async fn get(
319 &self,
320 model_type: &str,
321 model_id: &str,
322 relation: &str,
323 ) -> Option<serde_json::Value> {
324 let cache = self.cache.read().await;
325
326 cache.get(model_type)?.get(model_id)?.get(relation).cloned()
327 }
328
329 pub async fn contains(&self, model_type: &str, model_id: &str, relation: &str) -> bool {
331 let cache = self.cache.read().await;
332
333 cache
334 .get(model_type)
335 .and_then(|models| models.get(model_id))
336 .and_then(|relations| relations.get(relation))
337 .is_some()
338 }
339
340 pub async fn clear_model(&self, model_type: &str, model_id: &str) {
342 let mut cache = self.cache.write().await;
343
344 if let Some(models) = cache.get_mut(model_type) {
345 models.remove(model_id);
346 }
347 }
348
349 pub async fn clear_model_type(&self, model_type: &str) {
351 let mut cache = self.cache.write().await;
352 cache.remove(model_type);
353 }
354
355 pub async fn clear_all(&self) {
357 let mut cache = self.cache.write().await;
358 cache.clear();
359 }
360
361 pub async fn stats(&self) -> CacheStats {
363 let cache = self.cache.read().await;
364
365 let model_types = cache.len();
366 let total_models = cache.values().map(|m| m.len()).sum();
367 let total_relationships = cache
368 .values()
369 .flat_map(|models| models.values())
370 .map(|relations| relations.len())
371 .sum();
372
373 CacheStats {
374 model_types,
375 total_models,
376 total_relationships,
377 }
378 }
379}
380
381impl Default for RelationshipCache {
382 fn default() -> Self {
383 Self::new()
384 }
385}
386
387#[derive(Debug, Clone)]
389pub struct CacheStats {
390 pub model_types: usize,
391 pub total_models: usize,
392 pub total_relationships: usize,
393}
394
395static RELATIONSHIP_CACHE: tokio::sync::OnceCell<RelationshipCache> =
397 tokio::sync::OnceCell::const_new();
398
399pub async fn get_relationship_cache() -> &'static RelationshipCache {
401 RELATIONSHIP_CACHE
402 .get_or_init(|| async { RelationshipCache::new() })
403 .await
404}
405
406pub type LazyHasOne<T> = Lazy<Option<T>>;
408
409pub type LazyHasMany<T> = Lazy<Vec<T>>;
411
412pub type LazyBelongsTo<T> = Lazy<T>;
414
415pub trait LazyRelationshipBuilder<Parent, Related>
417where
418 Parent: Model + Send + Sync + Clone + 'static,
419 Related: Model + Send + Sync + Clone + 'static,
420{
421 fn lazy_has_one(parent: &Parent, foreign_key: String) -> LazyHasOne<Related> {
423 let parent_id = parent
424 .primary_key()
425 .map(|pk| pk.to_string())
426 .unwrap_or_default();
427 let loader = CachedRelationshipLoader::new(move |_pool| {
428 let _foreign_key = foreign_key.clone();
429 let _parent_id = parent_id.clone();
430 async move {
431 Ok(None)
435 }
436 });
437 Lazy::new(loader)
438 }
439
440 fn lazy_has_many(parent: &Parent, foreign_key: String) -> LazyHasMany<Related> {
442 let parent_id = parent
443 .primary_key()
444 .map(|pk| pk.to_string())
445 .unwrap_or_default();
446 let loader = CachedRelationshipLoader::new(move |_pool| {
447 let _foreign_key = foreign_key.clone();
448 let _parent_id = parent_id.clone();
449 async move {
450 Ok(Vec::<Related>::new())
454 }
455 });
456 Lazy::new(loader)
457 }
458
459 fn lazy_belongs_to(_child: &Related, _parent_id_field: String) -> LazyBelongsTo<Parent> {
461 let parent_id = "placeholder_id".to_string(); let loader = CachedRelationshipLoader::new(move |_pool| {
463 let _parent_id = parent_id.clone();
464 async move {
465 Err(crate::error::ModelError::Database(
469 "Placeholder implementation".to_string(),
470 ))
471 }
472 });
473 Lazy::new(loader)
474 }
475}