use async_trait::async_trait;
use serde::{Serialize, Deserialize, Serializer, Deserializer};
use std::collections::HashMap;
use std::marker::PhantomData;
use crate::error::{Error, Result};
use crate::model::Model;
use crate::query::{QueryBuilder, Order};
#[derive(Debug, Clone)]
pub struct SelfRef<E: Model> {
pub foreign_key: &'static str,
pub local_key: &'static str,
cached: Option<Box<E>>,
fk_value: Option<serde_json::Value>,
_marker: PhantomData<E>,
}
impl<E: Model> SelfRef<E> {
pub fn new(foreign_key: &'static str, local_key: &'static str) -> Self {
Self {
foreign_key,
local_key,
cached: None,
fk_value: None,
_marker: PhantomData,
}
}
pub fn with_fk_value(mut self, fk: serde_json::Value) -> Self {
self.fk_value = Some(fk);
self
}
pub async fn load(&self) -> Result<Option<E>> {
let fk = match &self.fk_value {
Some(v) if !v.is_null() => v,
_ => return Ok(None), };
E::query()
.where_eq(self.local_key, fk.clone())
.first()
.await
}
pub async fn load_with<F>(&self, constraint_fn: F) -> Result<Option<E>>
where
F: FnOnce(QueryBuilder<E>) -> QueryBuilder<E> + Send,
{
let fk = match &self.fk_value {
Some(v) if !v.is_null() => v,
_ => return Ok(None),
};
let query = E::query().where_eq(self.local_key, fk.clone());
constraint_fn(query).first().await
}
pub async fn exists(&self) -> Result<bool> {
let fk = match &self.fk_value {
Some(v) if !v.is_null() => v,
_ => return Ok(false),
};
E::query()
.where_eq(self.local_key, fk.clone())
.exists()
.await
}
pub fn get_cached(&self) -> Option<&E> {
self.cached.as_deref()
}
}
impl<E: Model> Default for SelfRef<E> {
fn default() -> Self {
Self {
foreign_key: "parent_id",
local_key: "id",
cached: None,
fk_value: None,
_marker: PhantomData,
}
}
}
impl<E: Model + Serialize> Serialize for SelfRef<E> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
self.cached.serialize(serializer)
}
}
impl<'de, E: Model> Deserialize<'de> for SelfRef<E> {
fn deserialize<D>(_deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self::default())
}
}
#[derive(Debug, Clone)]
pub struct SelfRefMany<E: Model> {
pub foreign_key: &'static str,
pub local_key: &'static str,
cached: Option<Vec<E>>,
parent_pk: Option<serde_json::Value>,
_marker: PhantomData<E>,
}
impl<E: Model> SelfRefMany<E> {
pub fn new(foreign_key: &'static str, local_key: &'static str) -> Self {
Self {
foreign_key,
local_key,
cached: None,
parent_pk: None,
_marker: PhantomData,
}
}
pub fn with_parent_pk(mut self, pk: serde_json::Value) -> Self {
self.parent_pk = Some(pk);
self
}
pub async fn load(&self) -> Result<Vec<E>> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for self-reference")))?;
E::query()
.where_eq(self.foreign_key, pk.clone())
.get()
.await
}
pub async fn load_with<F>(&self, constraint_fn: F) -> Result<Vec<E>>
where
F: FnOnce(QueryBuilder<E>) -> QueryBuilder<E> + Send,
{
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for self-reference")))?;
let query = E::query().where_eq(self.foreign_key, pk.clone());
constraint_fn(query).get().await
}
pub async fn count(&self) -> Result<u64> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for self-reference")))?;
E::query()
.where_eq(self.foreign_key, pk.clone())
.count()
.await
}
pub async fn exists(&self) -> Result<bool> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for self-reference")))?;
E::query()
.where_eq(self.foreign_key, pk.clone())
.exists()
.await
}
pub fn get_cached(&self) -> Option<&[E]> {
self.cached.as_deref()
}
pub async fn load_tree(&self, max_depth: usize) -> Result<Vec<E>> {
if max_depth == 0 {
return Ok(Vec::new());
}
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for self-reference")))?;
self.load_tree_recursive(pk.clone(), max_depth).await
}
#[async_recursion::async_recursion]
async fn load_tree_recursive(&self, parent_pk: serde_json::Value, depth: usize) -> Result<Vec<E>> {
if depth == 0 {
return Ok(Vec::new());
}
let children: Vec<E> = E::query()
.where_eq(self.foreign_key, parent_pk)
.get()
.await?;
let mut all = children.clone();
for child in children {
let pk_string = format!("{}", child.primary_key());
let child_pk = if let Ok(num) = pk_string.parse::<i64>() {
serde_json::Value::Number(num.into())
} else {
serde_json::Value::String(pk_string)
};
if !child_pk.is_null() {
let descendants = self.load_tree_recursive(child_pk, depth - 1).await?;
all.extend(descendants);
}
}
Ok(all)
}
}
impl<E: Model> Default for SelfRefMany<E> {
fn default() -> Self {
Self {
foreign_key: "parent_id",
local_key: "id",
cached: None,
parent_pk: None,
_marker: PhantomData,
}
}
}
impl<E: Model + Serialize> Serialize for SelfRefMany<E> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
self.cached.serialize(serializer)
}
}
impl<'de, E: Model> Deserialize<'de> for SelfRefMany<E> {
fn deserialize<D>(_deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self::default())
}
}
#[derive(Debug, Clone)]
pub struct HasOne<E: Model> {
pub foreign_key: &'static str,
pub local_key: &'static str,
cached: Option<Box<E>>,
parent_pk: Option<serde_json::Value>,
_marker: PhantomData<E>,
}
impl<E: Model> HasOne<E> {
pub fn new(foreign_key: &'static str, local_key: &'static str) -> Self {
Self {
foreign_key,
local_key,
cached: None,
parent_pk: None,
_marker: PhantomData,
}
}
pub fn with_parent_pk(mut self, pk: serde_json::Value) -> Self {
self.parent_pk = Some(pk);
self
}
pub async fn load(&self) -> Result<Option<E>> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
E::query()
.where_eq(self.foreign_key, pk.clone())
.first()
.await
}
pub async fn load_with<F>(&self, constraint_fn: F) -> Result<Option<E>>
where
F: FnOnce(QueryBuilder<E>) -> QueryBuilder<E> + Send,
{
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
let query = E::query().where_eq(self.foreign_key, pk.clone());
constraint_fn(query).first().await
}
pub async fn exists(&self) -> Result<bool> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
E::query()
.where_eq(self.foreign_key, pk.clone())
.exists()
.await
}
pub fn get_cached(&self) -> Option<&E> {
self.cached.as_deref()
}
}
impl<E: Model> Default for HasOne<E> {
fn default() -> Self {
Self {
foreign_key: "id",
local_key: "id",
cached: None,
parent_pk: None,
_marker: PhantomData,
}
}
}
impl<E: Model + Serialize> Serialize for HasOne<E> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
self.cached.serialize(serializer)
}
}
impl<'de, E: Model> Deserialize<'de> for HasOne<E> {
fn deserialize<D>(_deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self::default())
}
}
#[derive(Debug, Clone)]
pub struct HasMany<E: Model> {
pub foreign_key: &'static str,
pub local_key: &'static str,
cached: Option<Vec<E>>,
parent_pk: Option<serde_json::Value>,
_marker: PhantomData<E>,
}
impl<E: Model> HasMany<E> {
pub fn new(foreign_key: &'static str, local_key: &'static str) -> Self {
Self {
foreign_key,
local_key,
cached: None,
parent_pk: None,
_marker: PhantomData,
}
}
pub fn with_parent_pk(mut self, pk: serde_json::Value) -> Self {
self.parent_pk = Some(pk);
self
}
pub async fn load(&self) -> Result<Vec<E>> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
E::query()
.where_eq(self.foreign_key, pk.clone())
.get()
.await
}
pub async fn load_with<F>(&self, constraint_fn: F) -> Result<Vec<E>>
where
F: FnOnce(QueryBuilder<E>) -> QueryBuilder<E> + Send,
{
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
let query = E::query().where_eq(self.foreign_key, pk.clone());
constraint_fn(query).get().await
}
pub async fn count(&self) -> Result<u64> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
E::query()
.where_eq(self.foreign_key, pk.clone())
.count()
.await
}
pub async fn exists(&self) -> Result<bool> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
E::query()
.where_eq(self.foreign_key, pk.clone())
.exists()
.await
}
pub fn get_cached(&self) -> Option<&[E]> {
self.cached.as_deref()
}
}
impl<E: Model> Default for HasMany<E> {
fn default() -> Self {
Self {
foreign_key: "id",
local_key: "id",
cached: None,
parent_pk: None,
_marker: PhantomData,
}
}
}
impl<E: Model + Serialize> Serialize for HasMany<E> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
self.cached.serialize(serializer)
}
}
impl<'de, E: Model> Deserialize<'de> for HasMany<E> {
fn deserialize<D>(_deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self::default())
}
}
#[derive(Debug, Clone)]
pub struct BelongsTo<E: Model> {
pub foreign_key: &'static str,
pub owner_key: &'static str,
cached: Option<Box<E>>,
fk_value: Option<serde_json::Value>,
_marker: PhantomData<E>,
}
impl<E: Model> BelongsTo<E> {
pub fn new(foreign_key: &'static str, owner_key: &'static str) -> Self {
Self {
foreign_key,
owner_key,
cached: None,
fk_value: None,
_marker: PhantomData,
}
}
pub fn with_fk_value(mut self, fk: serde_json::Value) -> Self {
self.fk_value = Some(fk);
self
}
pub async fn load(&self) -> Result<Option<E>> {
let fk = self.fk_value.as_ref()
.ok_or_else(|| Error::query(String::from("Foreign key value not set for relation")))?;
E::query()
.where_eq(self.owner_key, fk.clone())
.first()
.await
}
pub async fn load_with<F>(&self, constraint_fn: F) -> Result<Option<E>>
where
F: FnOnce(QueryBuilder<E>) -> QueryBuilder<E> + Send,
{
let fk = self.fk_value.as_ref()
.ok_or_else(|| Error::query(String::from("Foreign key value not set for relation")))?;
let query = E::query().where_eq(self.owner_key, fk.clone());
constraint_fn(query).first().await
}
pub async fn exists(&self) -> Result<bool> {
let fk = self.fk_value.as_ref()
.ok_or_else(|| Error::query(String::from("Foreign key value not set for relation")))?;
E::query()
.where_eq(self.owner_key, fk.clone())
.exists()
.await
}
pub fn get_cached(&self) -> Option<&E> {
self.cached.as_deref()
}
}
impl<E: Model> Default for BelongsTo<E> {
fn default() -> Self {
Self {
foreign_key: "id",
owner_key: "id",
cached: None,
fk_value: None,
_marker: PhantomData,
}
}
}
impl<E: Model + Serialize> Serialize for BelongsTo<E> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
self.cached.serialize(serializer)
}
}
impl<'de, E: Model> Deserialize<'de> for BelongsTo<E> {
fn deserialize<D>(_deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self::default())
}
}
#[derive(Debug, Clone)]
pub struct HasManyThrough<Related: Model, Pivot: Model> {
pub foreign_key: &'static str,
pub related_key: &'static str,
pub local_key: &'static str,
pub related_local_key: &'static str,
pub pivot_table: &'static str,
cached: Option<Vec<Related>>,
parent_pk: Option<serde_json::Value>,
_marker: PhantomData<(Related, Pivot)>,
}
impl<Related: Model, Pivot: Model> HasManyThrough<Related, Pivot> {
pub fn new(
foreign_key: &'static str,
related_key: &'static str,
local_key: &'static str,
related_local_key: &'static str,
pivot_table: &'static str,
) -> Self {
Self {
foreign_key,
related_key,
local_key,
related_local_key,
pivot_table,
cached: None,
parent_pk: None,
_marker: PhantomData,
}
}
pub fn with_parent_pk(mut self, pk: serde_json::Value) -> Self {
self.parent_pk = Some(pk);
self
}
pub async fn load(&self) -> Result<Vec<Related>> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
Related::query()
.inner_join(
self.pivot_table,
&format!("{}.{}", self.pivot_table, self.related_key),
&format!("{}.{}", Related::table_name(), self.related_local_key),
)
.where_raw(&format!("{}.{} = {}", self.pivot_table, self.foreign_key, pk))
.get()
.await
}
pub async fn load_with<F>(&self, constraint_fn: F) -> Result<Vec<Related>>
where
F: FnOnce(QueryBuilder<Related>) -> QueryBuilder<Related> + Send,
{
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
let query = Related::query()
.inner_join(
self.pivot_table,
&format!("{}.{}", self.pivot_table, self.related_key),
&format!("{}.{}", Related::table_name(), self.related_local_key),
)
.where_raw(&format!("{}.{} = {}", self.pivot_table, self.foreign_key, pk));
constraint_fn(query).get().await
}
pub async fn count(&self) -> Result<u64> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
Pivot::query()
.where_raw(&format!("{}.{} = {}", self.pivot_table, self.foreign_key, pk))
.count()
.await
}
pub async fn attach(&self, related_id: impl Into<serde_json::Value>) -> Result<()> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
let mut data = HashMap::new();
data.insert(self.foreign_key.to_string(), pk.clone());
data.insert(self.related_key.to_string(), related_id.into());
let columns: Vec<&str> = data.keys().map(|s| s.as_str()).collect();
let placeholders: Vec<String> = (1..=columns.len()).map(|i| format!("${}", i)).collect();
let sql = format!(
"INSERT INTO {} ({}) VALUES ({})",
self.pivot_table,
columns.join(", "),
placeholders.join(", ")
);
let params: Vec<crate::internal::Value> = columns.iter()
.filter_map(|col| data.get(*col))
.map(|v| crate::internal::Value::from(v.clone()))
.collect();
crate::database::Database::execute_with_params(&sql, params).await?;
Ok(())
}
pub async fn detach(&self, related_id: impl Into<serde_json::Value>) -> Result<u64> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
Pivot::query()
.where_eq(self.foreign_key, pk.clone())
.where_eq(self.related_key, related_id.into())
.delete()
.await
}
pub async fn sync(&self, related_ids: Vec<serde_json::Value>) -> Result<()> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
Pivot::query()
.where_eq(self.foreign_key, pk.clone())
.delete()
.await?;
for id in related_ids {
self.attach(id).await?;
}
Ok(())
}
pub fn get_cached(&self) -> Option<&[Related]> {
self.cached.as_deref()
}
}
impl<Related: Model, Pivot: Model> Default for HasManyThrough<Related, Pivot> {
fn default() -> Self {
Self {
foreign_key: "id",
related_key: "id",
local_key: "id",
related_local_key: "id",
pivot_table: "",
cached: None,
parent_pk: None,
_marker: PhantomData,
}
}
}
impl<Related: Model + Serialize, Pivot: Model> Serialize for HasManyThrough<Related, Pivot> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
self.cached.serialize(serializer)
}
}
impl<'de, Related: Model, Pivot: Model> Deserialize<'de> for HasManyThrough<Related, Pivot> {
fn deserialize<D>(_deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self::default())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WithPivot<M, P> {
#[serde(flatten)]
pub model: M,
pub pivot: P,
}
impl<M, P> WithPivot<M, P> {
pub fn new(model: M, pivot: P) -> Self {
Self { model, pivot }
}
pub fn into_model(self) -> M {
self.model
}
pub fn pivot(&self) -> &P {
&self.pivot
}
pub fn into_parts(self) -> (M, P) {
(self.model, self.pivot)
}
}
impl<M, P> std::ops::Deref for WithPivot<M, P> {
type Target = M;
fn deref(&self) -> &Self::Target {
&self.model
}
}
#[derive(Debug, Clone)]
pub struct MorphTo<Morphable> {
pub type_column: &'static str,
pub id_column: &'static str,
type_value: Option<String>,
id_value: Option<serde_json::Value>,
_marker: PhantomData<Morphable>,
}
impl<Morphable> MorphTo<Morphable> {
pub fn new(type_column: &'static str, id_column: &'static str) -> Self {
Self {
type_column,
id_column,
type_value: None,
id_value: None,
_marker: PhantomData,
}
}
pub fn with_values(mut self, type_value: String, id_value: serde_json::Value) -> Self {
self.type_value = Some(type_value);
self.id_value = Some(id_value);
self
}
}
impl<Morphable> Default for MorphTo<Morphable> {
fn default() -> Self {
Self {
type_column: "",
id_column: "",
type_value: None,
id_value: None,
_marker: PhantomData,
}
}
}
impl<Morphable: Serialize> Serialize for MorphTo<Morphable> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_none()
}
}
impl<'de, Morphable> Deserialize<'de> for MorphTo<Morphable> {
fn deserialize<D>(_deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self::default())
}
}
#[derive(Debug, Clone)]
pub struct MorphOne<Related: Model> {
pub morph_name: &'static str,
pub local_key: &'static str,
cached: Option<Box<Related>>,
parent_pk: Option<serde_json::Value>,
parent_table: Option<String>,
_marker: PhantomData<Related>,
}
impl<Related: Model> MorphOne<Related> {
pub fn new(morph_name: &'static str, local_key: &'static str) -> Self {
Self {
morph_name,
local_key,
cached: None,
parent_pk: None,
parent_table: None,
_marker: PhantomData,
}
}
pub fn with_parent(mut self, pk: serde_json::Value, table: String) -> Self {
self.parent_pk = Some(pk);
self.parent_table = Some(table);
self
}
pub async fn load(&self) -> Result<Option<Related>> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
let table = self.parent_table.as_ref()
.ok_or_else(|| Error::query(String::from("Parent table not set for relation")))?;
let type_column = format!("{}_type", self.morph_name);
let id_column = format!("{}_id", self.morph_name);
Related::query()
.where_eq(&type_column, table.clone())
.where_eq(&id_column, pk.clone())
.first()
.await
}
pub fn get_cached(&self) -> Option<&Related> {
self.cached.as_deref()
}
}
impl<Related: Model> Default for MorphOne<Related> {
fn default() -> Self {
Self {
morph_name: "",
local_key: "id",
cached: None,
parent_pk: None,
parent_table: None,
_marker: PhantomData,
}
}
}
impl<Related: Model + Serialize> Serialize for MorphOne<Related> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
self.cached.serialize(serializer)
}
}
impl<'de, Related: Model> Deserialize<'de> for MorphOne<Related> {
fn deserialize<D>(_deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self::default())
}
}
#[derive(Debug, Clone)]
pub struct MorphMany<Related: Model> {
pub morph_name: &'static str,
pub local_key: &'static str,
cached: Option<Vec<Related>>,
parent_pk: Option<serde_json::Value>,
parent_table: Option<String>,
_marker: PhantomData<Related>,
}
impl<Related: Model> MorphMany<Related> {
pub fn new(morph_name: &'static str, local_key: &'static str) -> Self {
Self {
morph_name,
local_key,
cached: None,
parent_pk: None,
parent_table: None,
_marker: PhantomData,
}
}
pub fn with_parent(mut self, pk: serde_json::Value, table: String) -> Self {
self.parent_pk = Some(pk);
self.parent_table = Some(table);
self
}
pub async fn load(&self) -> Result<Vec<Related>> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
let table = self.parent_table.as_ref()
.ok_or_else(|| Error::query(String::from("Parent table not set for relation")))?;
let type_column = format!("{}_type", self.morph_name);
let id_column = format!("{}_id", self.morph_name);
Related::query()
.where_eq(&type_column, table.clone())
.where_eq(&id_column, pk.clone())
.get()
.await
}
pub async fn load_with<F>(&self, constraint_fn: F) -> Result<Vec<Related>>
where
F: FnOnce(QueryBuilder<Related>) -> QueryBuilder<Related> + Send,
{
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
let table = self.parent_table.as_ref()
.ok_or_else(|| Error::query(String::from("Parent table not set for relation")))?;
let type_column = format!("{}_type", self.morph_name);
let id_column = format!("{}_id", self.morph_name);
let query = Related::query()
.where_eq(&type_column, table.clone())
.where_eq(&id_column, pk.clone());
constraint_fn(query).get().await
}
pub async fn count(&self) -> Result<u64> {
let pk = self.parent_pk.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
let table = self.parent_table.as_ref()
.ok_or_else(|| Error::query(String::from("Parent table not set for relation")))?;
let type_column = format!("{}_type", self.morph_name);
let id_column = format!("{}_id", self.morph_name);
Related::query()
.where_eq(&type_column, table.clone())
.where_eq(&id_column, pk.clone())
.count()
.await
}
pub fn get_cached(&self) -> Option<&[Related]> {
self.cached.as_deref()
}
}
impl<Related: Model> Default for MorphMany<Related> {
fn default() -> Self {
Self {
morph_name: "",
local_key: "id",
cached: None,
parent_pk: None,
parent_table: None,
_marker: PhantomData,
}
}
}
impl<Related: Model + Serialize> Serialize for MorphMany<Related> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
self.cached.serialize(serializer)
}
}
impl<'de, Related: Model> Deserialize<'de> for MorphMany<Related> {
fn deserialize<D>(_deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self::default())
}
}
#[derive(Debug, Clone)]
pub enum MorphResult<A, B> {
TypeA(A),
TypeB(B),
Unknown(serde_json::Value),
}
impl<A, B> MorphResult<A, B> {
pub fn is_type_a(&self) -> bool {
matches!(self, MorphResult::TypeA(_))
}
pub fn is_type_b(&self) -> bool {
matches!(self, MorphResult::TypeB(_))
}
pub fn is_unknown(&self) -> bool {
matches!(self, MorphResult::Unknown(_))
}
pub fn as_type_a(&self) -> Option<&A> {
match self {
MorphResult::TypeA(a) => Some(a),
_ => None,
}
}
pub fn as_type_b(&self) -> Option<&B> {
match self {
MorphResult::TypeB(b) => Some(b),
_ => None,
}
}
pub fn into_type_a(self) -> Option<A> {
match self {
MorphResult::TypeA(a) => Some(a),
_ => None,
}
}
pub fn into_type_b(self) -> Option<B> {
match self {
MorphResult::TypeB(b) => Some(b),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub enum MorphResult3<A, B, C> {
TypeA(A),
TypeB(B),
TypeC(C),
Unknown(serde_json::Value),
}
#[derive(Debug, Clone)]
pub enum MorphResult4<A, B, C, D> {
TypeA(A),
TypeB(B),
TypeC(C),
TypeD(D),
Unknown(serde_json::Value),
}
#[derive(Debug, Clone, Default)]
pub struct RelationConstraints {
pub conditions: Vec<(String, serde_json::Value)>,
pub order_by: Option<(String, Order)>,
pub limit: Option<u64>,
pub offset: Option<u64>,
pub with_trashed: bool,
pub only_trashed: bool,
}
impl RelationConstraints {
pub fn new() -> Self {
Self::default()
}
pub fn where_eq(mut self, column: &str, value: impl Into<serde_json::Value>) -> Self {
self.conditions.push((column.to_string(), value.into()));
self
}
pub fn order_by(mut self, column: &str, order: Order) -> Self {
self.order_by = Some((column.to_string(), order));
self
}
pub fn limit(mut self, n: u64) -> Self {
self.limit = Some(n);
self
}
pub fn offset(mut self, n: u64) -> Self {
self.offset = Some(n);
self
}
pub fn with_trashed(mut self) -> Self {
self.with_trashed = true;
self
}
pub fn only_trashed(mut self) -> Self {
self.only_trashed = true;
self
}
pub fn apply<M: Model>(self, mut query: QueryBuilder<M>) -> QueryBuilder<M> {
for (column, value) in self.conditions {
query = query.where_eq(&column, value);
}
if let Some((column, order)) = self.order_by {
query = query.order_by(&column, order);
}
if let Some(limit) = self.limit {
query = query.limit(limit);
}
if let Some(offset) = self.offset {
query = query.offset(offset);
}
if self.with_trashed {
query = query.with_trashed();
}
if self.only_trashed {
query = query.only_trashed();
}
query
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WithRelations<M> {
#[serde(flatten)]
pub model: M,
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub relations: HashMap<String, serde_json::Value>,
}
impl<M: Model> WithRelations<M> {
pub fn new(model: M) -> Self {
Self {
model,
relations: HashMap::new(),
}
}
pub fn with_relation(mut self, name: &str, data: serde_json::Value) -> Self {
self.relations.insert(name.to_string(), data);
self
}
pub fn get_relation<R: for<'de> Deserialize<'de>>(&self, name: &str) -> Option<R> {
self.relations.get(name)
.and_then(|v| serde_json::from_value(v.clone()).ok())
}
pub fn has_relation(&self, name: &str) -> bool {
self.relations.contains_key(name)
}
pub fn into_inner(self) -> M {
self.model
}
}
impl<M> std::ops::Deref for WithRelations<M> {
type Target = M;
fn deref(&self) -> &Self::Target {
&self.model
}
}
impl<M> std::ops::DerefMut for WithRelations<M> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.model
}
}
#[derive(Debug, Clone)]
pub struct RelationPath {
pub full_path: String,
pub segments: Vec<String>,
}
impl RelationPath {
pub fn parse(path: &str) -> Self {
let segments: Vec<String> = path.split('.').map(|s| s.to_string()).collect();
Self {
full_path: path.to_string(),
segments,
}
}
pub fn root(&self) -> &str {
self.segments.first().map(|s| s.as_str()).unwrap_or("")
}
pub fn nested(&self) -> Option<RelationPath> {
if self.segments.len() > 1 {
Some(RelationPath {
full_path: self.segments[1..].join("."),
segments: self.segments[1..].to_vec(),
})
} else {
None
}
}
pub fn is_nested(&self) -> bool {
self.segments.len() > 1
}
pub fn depth(&self) -> usize {
self.segments.len()
}
}
#[derive(Debug, Clone, Default)]
pub struct RelationTree {
children: HashMap<String, RelationTree>,
}
impl RelationTree {
pub fn new() -> Self {
Self {
children: HashMap::new(),
}
}
pub fn add_path(&mut self, path: &RelationPath) {
if path.segments.is_empty() {
return;
}
let root = path.root().to_string();
let child = self.children.entry(root).or_default();
if let Some(nested) = path.nested() {
child.add_path(&nested);
}
}
pub fn roots(&self) -> Vec<String> {
self.children.keys().cloned().collect()
}
pub fn get_nested(&self, name: &str) -> Option<&RelationTree> {
self.children.get(name)
}
pub fn is_empty(&self) -> bool {
self.children.is_empty()
}
pub fn has_nested(&self, name: &str) -> bool {
self.children.get(name)
.map(|t| !t.is_empty())
.unwrap_or(false)
}
}
pub struct EagerQueryBuilder<M: Model> {
query: QueryBuilder<M>,
relation_tree: RelationTree,
}
impl<M: Model> EagerQueryBuilder<M> {
pub fn new() -> Self {
Self {
query: QueryBuilder::new(),
relation_tree: RelationTree::new(),
}
}
pub fn with(mut self, relation: &str) -> Self {
let path = RelationPath::parse(relation);
self.relation_tree.add_path(&path);
self
}
pub fn with_many(mut self, relations: &[&str]) -> Self {
for relation in relations {
self = self.with(relation);
}
self
}
pub fn where_eq<V: Into<serde_json::Value>>(mut self, column: &str, value: V) -> Self {
self.query = self.query.where_eq(column, value);
self
}
pub fn where_in<V: Into<serde_json::Value>>(mut self, column: &str, values: Vec<V>) -> Self {
self.query = self.query.where_in(column, values);
self
}
pub fn where_raw(mut self, sql: &str) -> Self {
self.query = self.query.where_raw(sql);
self
}
pub fn order_by(mut self, column: &str, order: Order) -> Self {
self.query = self.query.order_by(column, order);
self
}
pub fn limit(mut self, n: u64) -> Self {
self.query = self.query.limit(n);
self
}
pub fn offset(mut self, n: u64) -> Self {
self.query = self.query.offset(n);
self
}
pub fn get_relation_tree(&self) -> &RelationTree {
&self.relation_tree
}
pub async fn get(self) -> Result<Vec<WithRelations<M>>> {
let models = self.query.get().await?;
let results: Vec<WithRelations<M>> = models
.into_iter()
.map(WithRelations::new)
.collect();
Ok(results)
}
pub async fn first(mut self) -> Result<Option<WithRelations<M>>> {
self.query = self.query.limit(1);
let results = self.get().await?;
Ok(results.into_iter().next())
}
pub async fn find(mut self, id: impl Into<serde_json::Value>) -> Result<Option<WithRelations<M>>> {
self.query = self.query.where_eq(M::primary_key_name(), id);
self.first().await
}
}
impl<M: Model> Default for EagerQueryBuilder<M> {
fn default() -> Self {
Self::new()
}
}
pub struct RelationLoader<M> {
pub name: String,
#[allow(clippy::type_complexity)]
pub loader: Box<dyn Fn(&[M]) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<HashMap<String, serde_json::Value>>> + Send>> + Send + Sync>,
}
#[derive(Debug, Clone)]
pub struct RelationInfo {
pub name: String,
pub relation_type: RelationType,
pub related_table: String,
pub foreign_key: String,
pub local_key: String,
pub pivot_table: Option<String>,
pub morph_type_column: Option<String>,
pub morph_id_column: Option<String>,
}
impl RelationInfo {
pub fn belongs_to(name: &str, related_table: &str, foreign_key: &str, local_key: &str) -> Self {
Self {
name: name.to_string(),
relation_type: RelationType::BelongsTo,
related_table: related_table.to_string(),
foreign_key: foreign_key.to_string(),
local_key: local_key.to_string(),
pivot_table: None,
morph_type_column: None,
morph_id_column: None,
}
}
pub fn has_one(name: &str, related_table: &str, foreign_key: &str, local_key: &str) -> Self {
Self {
name: name.to_string(),
relation_type: RelationType::HasOne,
related_table: related_table.to_string(),
foreign_key: foreign_key.to_string(),
local_key: local_key.to_string(),
pivot_table: None,
morph_type_column: None,
morph_id_column: None,
}
}
pub fn has_many(name: &str, related_table: &str, foreign_key: &str, local_key: &str) -> Self {
Self {
name: name.to_string(),
relation_type: RelationType::HasMany,
related_table: related_table.to_string(),
foreign_key: foreign_key.to_string(),
local_key: local_key.to_string(),
pivot_table: None,
morph_type_column: None,
morph_id_column: None,
}
}
pub fn has_many_through(
name: &str,
related_table: &str,
pivot_table: &str,
foreign_key: &str,
related_key: &str,
local_key: &str,
) -> Self {
Self {
name: name.to_string(),
relation_type: RelationType::HasManyThrough,
related_table: related_table.to_string(),
foreign_key: foreign_key.to_string(),
local_key: local_key.to_string(),
pivot_table: Some(pivot_table.to_string()),
morph_type_column: Some(related_key.to_string()),
morph_id_column: None,
}
}
pub fn morph_one(
name: &str,
related_table: &str,
type_column: &str,
id_column: &str,
local_key: &str,
) -> Self {
Self {
name: name.to_string(),
relation_type: RelationType::MorphOne,
related_table: related_table.to_string(),
foreign_key: String::new(),
local_key: local_key.to_string(),
pivot_table: None,
morph_type_column: Some(type_column.to_string()),
morph_id_column: Some(id_column.to_string()),
}
}
pub fn morph_many(
name: &str,
related_table: &str,
type_column: &str,
id_column: &str,
local_key: &str,
) -> Self {
Self {
name: name.to_string(),
relation_type: RelationType::MorphMany,
related_table: related_table.to_string(),
foreign_key: String::new(),
local_key: local_key.to_string(),
pivot_table: None,
morph_type_column: Some(type_column.to_string()),
morph_id_column: Some(id_column.to_string()),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RelationType {
BelongsTo,
HasOne,
HasMany,
HasManyThrough,
MorphTo,
MorphOne,
MorphMany,
}
impl std::fmt::Display for RelationType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RelationType::BelongsTo => write!(f, "belongs_to"),
RelationType::HasOne => write!(f, "has_one"),
RelationType::HasMany => write!(f, "has_many"),
RelationType::HasManyThrough => write!(f, "has_many_through"),
RelationType::MorphTo => write!(f, "morph_to"),
RelationType::MorphOne => write!(f, "morph_one"),
RelationType::MorphMany => write!(f, "morph_many"),
}
}
}
pub trait EagerLoadExt: Model {
fn eager() -> EagerQueryBuilder<Self>
where
Self: Sized,
{
EagerQueryBuilder::new()
}
fn with_relation(relation_name: &str) -> EagerQueryBuilder<Self>
where
Self: Sized,
{
EagerQueryBuilder::new().with(relation_name)
}
fn with_relations(relations: &[&str]) -> EagerQueryBuilder<Self>
where
Self: Sized,
{
EagerQueryBuilder::new().with_many(relations)
}
}
impl<T: Model> EagerLoadExt for T {}
#[async_trait]
pub trait RelationExt: Model {
fn get_field_value(&self, field: &str) -> Result<serde_json::Value> {
let json = serde_json::to_value(self)
.map_err(|e| Error::query(format!("Failed to serialize model: {}", e)))?;
json.get(field)
.cloned()
.ok_or_else(|| Error::query(format!("Field '{}' not found on model", field)))
}
}
impl<T: Model> RelationExt for T {}