1use crate::entity::{IEntityType, IFromRow, ILazyInit};
24use crate::error::{EFError, EFResult};
25use crate::lazy::{load_collection_lazy, load_scalar_lazy, LazyContext, MAX_LAZY_DEPTH};
26use std::marker::PhantomData;
27use std::sync::Arc;
28
29pub struct BelongsTo<T> {
38 inner: Option<Box<T>>,
39 lazy_ctx: Option<Arc<dyn LazyContext>>,
40 loaded: bool,
41 _phantom: PhantomData<T>,
42}
43
44impl<T> BelongsTo<T> {
45 pub fn new() -> Self {
46 Self {
47 inner: None,
48 lazy_ctx: None,
49 loaded: false,
50 _phantom: PhantomData,
51 }
52 }
53
54 pub fn with(entity: T) -> Self {
55 Self {
56 inner: Some(Box::new(entity)),
57 lazy_ctx: None,
58 loaded: true,
59 _phantom: PhantomData,
60 }
61 }
62
63 pub fn get(&self) -> Option<&T> {
64 self.inner.as_deref()
65 }
66
67 pub fn get_mut(&mut self) -> Option<&mut T> {
68 self.inner.as_deref_mut()
69 }
70
71 pub fn take(&mut self) -> Option<T> {
75 let taken = self.inner.take();
76 self.loaded = false;
77 taken.map(|b| *b)
78 }
79
80 pub fn is_loaded(&self) -> bool {
83 self.loaded
84 }
85
86 pub fn set_lazy_context(&mut self, ctx: Arc<dyn LazyContext>) {
91 self.lazy_ctx = Some(ctx);
92 }
93
94 pub async fn load(&mut self) -> EFResult<()>
103 where
104 T: IFromRow + IEntityType + ILazyInit,
105 {
106 if self.loaded {
107 return Ok(());
108 }
109 let Some(ctx) = self.lazy_ctx.clone() else {
110 return Ok(());
112 };
113 if ctx.depth() >= MAX_LAZY_DEPTH {
114 return Err(EFError::other("lazy loading recursion limit exceeded"));
115 }
116 let maybe_entity = load_scalar_lazy::<T>(ctx.as_ref()).await?;
117 if let Some(mut entity) = maybe_entity {
118 let provider = ctx.provider().clone();
120 let filter_map = ctx.filter_map().map(|m| Arc::new(m.clone()));
121 entity.attach_lazy_contexts(provider, filter_map, ctx.depth() + 1);
122 self.inner = Some(Box::new(entity));
123 }
124 self.loaded = true;
125 Ok(())
126 }
127}
128
129impl<T> Default for BelongsTo<T> {
130 fn default() -> Self {
131 Self::new()
132 }
133}
134
135impl<T> Clone for BelongsTo<T> {
136 fn clone(&self) -> Self {
137 Self {
138 inner: None, lazy_ctx: None,
140 loaded: false,
141 _phantom: PhantomData,
142 }
143 }
144}
145
146impl<T> std::fmt::Debug for BelongsTo<T> {
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 f.debug_struct("BelongsTo")
149 .field("loaded", &self.loaded)
150 .finish()
151 }
152}
153
154pub struct HasMany<T, Join = ()> {
163 items: Vec<T>,
164 lazy_ctx: Option<Arc<dyn LazyContext>>,
165 loaded: bool,
166 _phantom: PhantomData<(T, Join)>,
167}
168
169impl<T, Join> HasMany<T, Join> {
170 pub fn new() -> Self {
171 Self {
172 items: Vec::new(),
173 lazy_ctx: None,
174 loaded: false,
175 _phantom: PhantomData,
176 }
177 }
178
179 pub fn with(items: Vec<T>) -> Self {
180 Self {
181 items,
182 lazy_ctx: None,
183 loaded: true,
184 _phantom: PhantomData,
185 }
186 }
187
188 pub fn items(&self) -> &[T] {
189 &self.items
190 }
191
192 pub fn items_mut(&mut self) -> &mut Vec<T> {
193 &mut self.items
194 }
195
196 pub fn add(&mut self, item: T) {
197 self.items.push(item);
198 }
199
200 pub fn remove(&mut self, index: usize) -> Option<T> {
201 if index < self.items.len() {
202 Some(self.items.remove(index))
203 } else {
204 None
205 }
206 }
207
208 pub fn len(&self) -> usize {
209 self.items.len()
210 }
211
212 pub fn is_empty(&self) -> bool {
213 self.items.is_empty()
214 }
215
216 pub fn is_loaded(&self) -> bool {
219 self.loaded
220 }
221
222 pub fn set_lazy_context(&mut self, ctx: Arc<dyn LazyContext>) {
227 self.lazy_ctx = Some(ctx);
228 }
229
230 pub async fn load(&mut self) -> EFResult<()>
238 where
239 T: IFromRow + IEntityType + ILazyInit,
240 Join: 'static,
241 {
242 if self.loaded {
243 return Ok(());
244 }
245 let Some(ctx) = self.lazy_ctx.clone() else {
246 return Ok(());
248 };
249 if ctx.depth() >= MAX_LAZY_DEPTH {
250 return Err(EFError::other("lazy loading recursion limit exceeded"));
251 }
252 let mut entities = load_collection_lazy::<T>(ctx.as_ref()).await?;
253 let provider = ctx.provider().clone();
255 let filter_map = ctx.filter_map().map(|m| Arc::new(m.clone()));
256 let depth = ctx.depth() + 1;
257 for entity in &mut entities {
258 let p = provider.clone();
260 let fm = filter_map.clone();
261 entity.attach_lazy_contexts(p, fm, depth);
262 }
263 self.items = entities;
264 self.loaded = true;
265 Ok(())
266 }
267}
268
269impl<T, Join> Default for HasMany<T, Join> {
270 fn default() -> Self {
271 Self::new()
272 }
273}
274
275impl<T, Join> Clone for HasMany<T, Join> {
276 fn clone(&self) -> Self {
277 Self {
278 items: Vec::new(), lazy_ctx: None,
280 loaded: false,
281 _phantom: PhantomData,
282 }
283 }
284}
285
286impl<T, Join> std::fmt::Debug for HasMany<T, Join> {
287 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288 f.debug_struct("HasMany")
289 .field("loaded", &self.loaded)
290 .field("len", &self.items.len())
291 .finish()
292 }
293}
294
295pub type Through<Join> = Join;
297
298pub struct HasOne<T> {
305 inner: Option<Box<T>>,
306 lazy_ctx: Option<Arc<dyn LazyContext>>,
307 loaded: bool,
308 _phantom: PhantomData<T>,
309}
310
311impl<T> HasOne<T> {
312 pub fn new() -> Self {
313 Self {
314 inner: None,
315 lazy_ctx: None,
316 loaded: false,
317 _phantom: PhantomData,
318 }
319 }
320
321 pub fn with(entity: T) -> Self {
322 Self {
323 inner: Some(Box::new(entity)),
324 lazy_ctx: None,
325 loaded: true,
326 _phantom: PhantomData,
327 }
328 }
329
330 pub fn get(&self) -> Option<&T> {
331 self.inner.as_deref()
332 }
333
334 pub fn get_mut(&mut self) -> Option<&mut T> {
335 self.inner.as_deref_mut()
336 }
337
338 pub fn take(&mut self) -> Option<T> {
342 let taken = self.inner.take();
343 self.loaded = false;
344 taken.map(|b| *b)
345 }
346
347 pub fn is_loaded(&self) -> bool {
349 self.loaded
350 }
351
352 pub fn set_lazy_context(&mut self, ctx: Arc<dyn LazyContext>) {
354 self.lazy_ctx = Some(ctx);
355 }
356
357 pub async fn load(&mut self) -> EFResult<()>
359 where
360 T: IFromRow + IEntityType + ILazyInit,
361 {
362 if self.loaded {
363 return Ok(());
364 }
365 let Some(ctx) = self.lazy_ctx.clone() else {
366 return Ok(());
367 };
368 if ctx.depth() >= MAX_LAZY_DEPTH {
369 return Err(EFError::other("lazy loading recursion limit exceeded"));
370 }
371 let maybe_entity = load_scalar_lazy::<T>(ctx.as_ref()).await?;
372 if let Some(mut entity) = maybe_entity {
373 let provider = ctx.provider().clone();
374 let filter_map = ctx.filter_map().map(|m| Arc::new(m.clone()));
375 entity.attach_lazy_contexts(provider, filter_map, ctx.depth() + 1);
376 self.inner = Some(Box::new(entity));
377 }
378 self.loaded = true;
379 Ok(())
380 }
381}
382
383impl<T> Default for HasOne<T> {
384 fn default() -> Self {
385 Self::new()
386 }
387}
388
389impl<T> Clone for HasOne<T> {
390 fn clone(&self) -> Self {
391 Self {
392 inner: None,
393 lazy_ctx: None,
394 loaded: false,
395 _phantom: PhantomData,
396 }
397 }
398}
399
400impl<T> std::fmt::Debug for HasOne<T> {
401 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
402 f.debug_struct("HasOne")
403 .field("loaded", &self.loaded)
404 .finish()
405 }
406}
407
408#[derive(Debug, Clone, Copy, PartialEq, Eq)]
415pub enum DeleteBehavior {
416 Cascade,
417 Restrict,
418 SetNull,
419 NoAction,
420}
421
422impl DeleteBehavior {
423 pub fn to_sql_clause(self) -> &'static str {
425 match self {
426 DeleteBehavior::Cascade => "CASCADE",
427 DeleteBehavior::Restrict => "RESTRICT",
428 DeleteBehavior::SetNull => "SET NULL",
429 DeleteBehavior::NoAction => "NO ACTION",
430 }
431 }
432}