use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::marker::PhantomData;
use crate::error::{Error, Result};
use crate::model::Model;
use crate::query::QueryBuilder;
use super::helpers::{cached_ref, ensure_relation_configured, preserve_cached_value};
use super::require_scalar_relation_key;
#[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
}
#[doc(hidden)]
pub fn preserve_runtime_state_from(&mut self, previous: &Self) {
if self.type_column == previous.type_column && self.id_column == previous.id_column {
self.type_value = previous.type_value.clone();
self.id_value = previous.id_value.clone();
}
}
}
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> {
fn ensure_configured(&self) -> Result<()> {
ensure_relation_configured("MorphOne", &[self.morph_name, self.local_key])
}
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
}
#[doc(hidden)]
pub fn preserve_runtime_state_from(&mut self, previous: &Self) {
preserve_cached_value(
&mut self.cached,
&previous.cached,
previous.parent_pk.is_none() && previous.parent_table.is_none(),
self.morph_name == previous.morph_name
&& self.local_key == previous.local_key
&& self.parent_pk == previous.parent_pk
&& self.parent_table == previous.parent_table,
);
}
pub async fn load(&self) -> Result<Option<Related>> {
if let Some(cached) = &self.cached {
return Ok(Some((**cached).clone()));
}
self.ensure_configured()?;
let pk = self
.parent_pk
.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
let pk = require_scalar_relation_key(pk, "MorphOne::load")?;
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> {
cached_ref(&self.cached)
}
}
impl<Related: Model> Default for MorphOne<Related> {
fn default() -> Self {
Self {
morph_name: "",
local_key: "",
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>,
{
let cached = Option::<Related>::deserialize(deserializer)?;
Ok(Self {
cached: cached.map(Box::new),
..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> {
fn ensure_configured(&self) -> Result<()> {
ensure_relation_configured("MorphMany", &[self.morph_name, self.local_key])
}
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
}
#[doc(hidden)]
pub fn preserve_runtime_state_from(&mut self, previous: &Self) {
preserve_cached_value(
&mut self.cached,
&previous.cached,
previous.parent_pk.is_none() && previous.parent_table.is_none(),
self.morph_name == previous.morph_name
&& self.local_key == previous.local_key
&& self.parent_pk == previous.parent_pk
&& self.parent_table == previous.parent_table,
);
}
pub async fn load(&self) -> Result<Vec<Related>> {
if let Some(cached) = &self.cached {
return Ok(cached.clone());
}
self.ensure_configured()?;
let pk = self
.parent_pk
.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
let pk = require_scalar_relation_key(pk, "MorphMany::load")?;
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,
{
self.ensure_configured()?;
let pk = self
.parent_pk
.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
let pk = require_scalar_relation_key(pk, "MorphMany::load_with")?;
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> {
self.ensure_configured()?;
let pk = self
.parent_pk
.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
let pk = require_scalar_relation_key(pk, "MorphMany::count")?;
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]> {
cached_ref(&self.cached)
}
}
impl<Related: Model> Default for MorphMany<Related> {
fn default() -> Self {
Self {
morph_name: "",
local_key: "",
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>,
{
let cached = Option::<Vec<Related>>::deserialize(deserializer)?;
Ok(Self {
cached,
..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),
}