use std::marker::PhantomData;
use crate::model::Model;
use crate::Error;
pub trait RelationKind: Send + Sync + 'static {}
pub struct HasMany;
impl RelationKind for HasMany {}
pub struct HasOne;
impl RelationKind for HasOne {}
pub struct BelongsTo;
impl RelationKind for BelongsTo {}
pub struct BelongsToMany;
impl RelationKind for BelongsToMany {}
pub trait RelationDef: Send + Sync {
type Parent: Model;
type Child: Model;
type Kind: RelationKind;
fn local_key() -> &'static str;
fn foreign_key() -> &'static str;
}
pub struct Relation<P: Model, C: Model, K: RelationKind> {
pub parent_value: serde_json::Value,
pub local_key: &'static str,
pub foreign_key: &'static str,
_marker: PhantomData<(P, C, K)>,
}
impl<P: Model, C: Model> Relation<P, C, HasMany> {
pub async fn load(self, pool: &sqlx::PgPool) -> Result<Vec<C>, Error> {
let sql = format!(
"SELECT {} FROM {} WHERE {} = $1",
C::COLUMNS.join(", "),
C::TABLE,
self.foreign_key,
);
let rows = sqlx::query_as::<_, C>(&sql)
.bind(self.parent_value)
.fetch_all(pool)
.await?;
Ok(rows)
}
}
impl<P: Model, C: Model> Relation<P, C, BelongsTo> {
pub async fn load(self, pool: &sqlx::PgPool) -> Result<Option<C>, Error> {
let sql = format!(
"SELECT {} FROM {} WHERE {} = $1 LIMIT 1",
C::COLUMNS.join(", "),
C::TABLE,
self.local_key,
);
let row = sqlx::query_as::<_, C>(&sql)
.bind(self.parent_value)
.fetch_optional(pool)
.await?;
Ok(row)
}
}
impl<P: Model, C: Model> Relation<P, C, HasOne> {
pub async fn load(self, pool: &sqlx::PgPool) -> Result<Option<C>, Error> {
let sql = format!(
"SELECT {} FROM {} WHERE {} = $1 LIMIT 1",
C::COLUMNS.join(", "),
C::TABLE,
self.foreign_key,
);
let row = sqlx::query_as::<_, C>(&sql)
.bind(self.parent_value)
.fetch_optional(pool)
.await?;
Ok(row)
}
}
impl<P: Model, C: Model, K: RelationKind> Relation<P, C, K> {
pub fn new(
parent_value: serde_json::Value,
local_key: &'static str,
foreign_key: &'static str,
) -> Self {
Self {
parent_value,
local_key,
foreign_key,
_marker: PhantomData,
}
}
}