use crate::entity::{IEntityType, IFromRow, ILazyInit};
use crate::error::{EFError, EFResult};
use crate::lazy::{load_collection_lazy, load_scalar_lazy, LazyContext, MAX_LAZY_DEPTH};
use std::marker::PhantomData;
use std::sync::Arc;
pub struct BelongsTo<T> {
inner: Option<Box<T>>,
lazy_ctx: Option<Arc<dyn LazyContext>>,
loaded: bool,
_phantom: PhantomData<T>,
}
impl<T> BelongsTo<T> {
pub fn new() -> Self {
Self {
inner: None,
lazy_ctx: None,
loaded: false,
_phantom: PhantomData,
}
}
pub fn with(entity: T) -> Self {
Self {
inner: Some(Box::new(entity)),
lazy_ctx: None,
loaded: true,
_phantom: PhantomData,
}
}
pub fn get(&self) -> Option<&T> {
self.inner.as_deref()
}
pub fn get_mut(&mut self) -> Option<&mut T> {
self.inner.as_deref_mut()
}
pub fn take(&mut self) -> Option<T> {
let taken = self.inner.take();
self.loaded = false;
taken.map(|b| *b)
}
pub fn is_loaded(&self) -> bool {
self.loaded
}
pub fn set_lazy_context(&mut self, ctx: Arc<dyn LazyContext>) {
self.lazy_ctx = Some(ctx);
}
pub async fn load(&mut self) -> EFResult<()>
where
T: IFromRow + IEntityType + ILazyInit,
{
if self.loaded {
return Ok(());
}
let Some(ctx) = self.lazy_ctx.clone() else {
return Ok(());
};
if ctx.depth() >= MAX_LAZY_DEPTH {
return Err(EFError::other("lazy loading recursion limit exceeded"));
}
let maybe_entity = load_scalar_lazy::<T>(ctx.as_ref()).await?;
if let Some(mut entity) = maybe_entity {
let provider = ctx.provider().clone();
let filter_map = ctx.filter_map().map(|m| Arc::new(m.clone()));
entity.attach_lazy_contexts(provider, filter_map, ctx.depth() + 1);
self.inner = Some(Box::new(entity));
}
self.loaded = true;
Ok(())
}
}
impl<T> Default for BelongsTo<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Clone for BelongsTo<T> {
fn clone(&self) -> Self {
Self {
inner: None, lazy_ctx: None,
loaded: false,
_phantom: PhantomData,
}
}
}
impl<T> std::fmt::Debug for BelongsTo<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BelongsTo")
.field("loaded", &self.loaded)
.finish()
}
}
pub struct HasMany<T, Join = ()> {
items: Vec<T>,
lazy_ctx: Option<Arc<dyn LazyContext>>,
loaded: bool,
_phantom: PhantomData<(T, Join)>,
}
impl<T, Join> HasMany<T, Join> {
pub fn new() -> Self {
Self {
items: Vec::new(),
lazy_ctx: None,
loaded: false,
_phantom: PhantomData,
}
}
pub fn with(items: Vec<T>) -> Self {
Self {
items,
lazy_ctx: None,
loaded: true,
_phantom: PhantomData,
}
}
pub fn items(&self) -> &[T] {
&self.items
}
pub fn items_mut(&mut self) -> &mut Vec<T> {
&mut self.items
}
pub fn add(&mut self, item: T) {
self.items.push(item);
}
pub fn remove(&mut self, index: usize) -> Option<T> {
if index < self.items.len() {
Some(self.items.remove(index))
} else {
None
}
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn is_loaded(&self) -> bool {
self.loaded
}
pub fn set_lazy_context(&mut self, ctx: Arc<dyn LazyContext>) {
self.lazy_ctx = Some(ctx);
}
pub async fn load(&mut self) -> EFResult<()>
where
T: IFromRow + IEntityType + ILazyInit,
Join: 'static,
{
if self.loaded {
return Ok(());
}
let Some(ctx) = self.lazy_ctx.clone() else {
return Ok(());
};
if ctx.depth() >= MAX_LAZY_DEPTH {
return Err(EFError::other("lazy loading recursion limit exceeded"));
}
let mut entities = load_collection_lazy::<T>(ctx.as_ref()).await?;
let provider = ctx.provider().clone();
let filter_map = ctx.filter_map().map(|m| Arc::new(m.clone()));
let depth = ctx.depth() + 1;
for entity in &mut entities {
let p = provider.clone();
let fm = filter_map.clone();
entity.attach_lazy_contexts(p, fm, depth);
}
self.items = entities;
self.loaded = true;
Ok(())
}
}
impl<T, Join> Default for HasMany<T, Join> {
fn default() -> Self {
Self::new()
}
}
impl<T, Join> Clone for HasMany<T, Join> {
fn clone(&self) -> Self {
Self {
items: Vec::new(), lazy_ctx: None,
loaded: false,
_phantom: PhantomData,
}
}
}
impl<T, Join> std::fmt::Debug for HasMany<T, Join> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HasMany")
.field("loaded", &self.loaded)
.field("len", &self.items.len())
.finish()
}
}
pub type Through<Join> = Join;
pub struct HasOne<T> {
inner: Option<Box<T>>,
lazy_ctx: Option<Arc<dyn LazyContext>>,
loaded: bool,
_phantom: PhantomData<T>,
}
impl<T> HasOne<T> {
pub fn new() -> Self {
Self {
inner: None,
lazy_ctx: None,
loaded: false,
_phantom: PhantomData,
}
}
pub fn with(entity: T) -> Self {
Self {
inner: Some(Box::new(entity)),
lazy_ctx: None,
loaded: true,
_phantom: PhantomData,
}
}
pub fn get(&self) -> Option<&T> {
self.inner.as_deref()
}
pub fn get_mut(&mut self) -> Option<&mut T> {
self.inner.as_deref_mut()
}
pub fn take(&mut self) -> Option<T> {
let taken = self.inner.take();
self.loaded = false;
taken.map(|b| *b)
}
pub fn is_loaded(&self) -> bool {
self.loaded
}
pub fn set_lazy_context(&mut self, ctx: Arc<dyn LazyContext>) {
self.lazy_ctx = Some(ctx);
}
pub async fn load(&mut self) -> EFResult<()>
where
T: IFromRow + IEntityType + ILazyInit,
{
if self.loaded {
return Ok(());
}
let Some(ctx) = self.lazy_ctx.clone() else {
return Ok(());
};
if ctx.depth() >= MAX_LAZY_DEPTH {
return Err(EFError::other("lazy loading recursion limit exceeded"));
}
let maybe_entity = load_scalar_lazy::<T>(ctx.as_ref()).await?;
if let Some(mut entity) = maybe_entity {
let provider = ctx.provider().clone();
let filter_map = ctx.filter_map().map(|m| Arc::new(m.clone()));
entity.attach_lazy_contexts(provider, filter_map, ctx.depth() + 1);
self.inner = Some(Box::new(entity));
}
self.loaded = true;
Ok(())
}
}
impl<T> Default for HasOne<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Clone for HasOne<T> {
fn clone(&self) -> Self {
Self {
inner: None,
lazy_ctx: None,
loaded: false,
_phantom: PhantomData,
}
}
}
impl<T> std::fmt::Debug for HasOne<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HasOne")
.field("loaded", &self.loaded)
.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeleteBehavior {
Cascade,
Restrict,
SetNull,
NoAction,
}
impl DeleteBehavior {
pub fn to_sql_clause(self) -> &'static str {
match self {
DeleteBehavior::Cascade => "CASCADE",
DeleteBehavior::Restrict => "RESTRICT",
DeleteBehavior::SetNull => "SET NULL",
DeleteBehavior::NoAction => "NO ACTION",
}
}
}