#![allow(missing_docs)]
use std::future::Future;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::sync::Arc;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::error::{Error, Result};
use crate::model::{Model, ModelMeta};
use crate::relations::require_scalar_relation_key;
use super::{__model_entity_manager_key, EntityManager, TideEntityManagerMeta};
#[derive(Debug, Clone)]
pub struct TrackedHasMany<T: Model> {
pub foreign_key: &'static str,
pub local_key: &'static str,
pub relation_name: &'static str,
pub owner_table: &'static str,
pub child_table: &'static str,
owner_key: Option<String>,
plain: crate::relations::DirectHasMany<T>,
cached: Option<Vec<T>>,
parent_pk: Option<serde_json::Value>,
entity_manager: Option<Arc<EntityManager>>,
_marker: PhantomData<T>,
}
impl<T: Model> Default for TrackedHasMany<T> {
fn default() -> Self {
Self::new("", "")
}
}
impl<T: Model> TrackedHasMany<T> {
fn ensure_configured(&self) -> Result<()> {
if self.foreign_key.is_empty() || self.local_key.is_empty() {
return Err(Error::invalid_query(
"HasMany relation is not configured; call with_relations() before loading"
.to_string(),
));
}
Ok(())
}
pub fn new(foreign_key: &'static str, local_key: &'static str) -> Self {
Self {
foreign_key,
local_key,
relation_name: "",
owner_table: "",
child_table: "",
owner_key: None,
plain: crate::relations::DirectHasMany::new(foreign_key, local_key),
cached: None,
parent_pk: None,
entity_manager: None,
_marker: PhantomData,
}
}
pub fn with_metadata(
mut self,
relation_name: &'static str,
owner_table: &'static str,
child_table: &'static str,
) -> Self {
self.relation_name = relation_name;
self.owner_table = owner_table;
self.child_table = child_table;
self
}
pub fn with_owner_key(mut self, owner_key: String) -> Self {
self.owner_key = Some(owner_key);
self
}
pub fn with_parent_pk(mut self, pk: serde_json::Value) -> Self {
self.plain = self.plain.with_parent_pk(pk.clone());
self.parent_pk = Some(pk);
self
}
#[doc(hidden)]
pub fn set_cached(&mut self, models: Vec<T>) {
self.plain.set_cached(models.clone());
self.cached = Some(models);
}
#[doc(hidden)]
pub fn preserve_runtime_state_from(&mut self, previous: &Self) {
let same_relation = self.foreign_key == previous.foreign_key
&& self.local_key == previous.local_key
&& self.parent_pk == previous.parent_pk
&& self.relation_name == previous.relation_name
&& self.owner_table == previous.owner_table
&& self.child_table == previous.child_table;
let allow_cached_without_context = previous.parent_pk.is_none();
if same_relation {
self.plain.preserve_runtime_state_from(&previous.plain);
if self.entity_manager.is_none() {
self.entity_manager = previous.entity_manager.clone();
}
if self.owner_key.is_none() {
self.owner_key = previous.owner_key.clone();
}
}
if ((allow_cached_without_context && previous.cached.is_some()) || same_relation)
&& self.cached.is_none()
{
self.cached = previous.cached.clone();
if let Some(cached) = self.cached.clone() {
self.plain.set_cached(cached);
}
}
}
pub fn as_mut(&mut self) -> Option<&mut Vec<T>> {
self.cached.as_mut()
}
pub fn items(&self) -> Option<&Vec<T>> {
self.cached.as_ref()
}
pub fn get_cached(&self) -> Option<&[T]> {
self.cached.as_deref()
}
pub fn is_loaded(&self) -> bool {
self.cached.is_some()
}
pub fn current_keys(&self) -> Result<Vec<String>>
where
T: Model + ModelMeta,
{
let mut keys = Vec::new();
for item in self.cached.as_deref().unwrap_or(&[]) {
if let Some(key) = __model_entity_manager_key(item)? {
keys.push(key);
}
}
Ok(keys)
}
#[doc(hidden)]
pub fn attach_query_database(&mut self, database: &crate::database::Database) {
self.plain.attach_query_database(database);
}
pub async fn load_in_entity_manager(
&mut self,
entity_manager: &Arc<EntityManager>,
) -> Result<&Vec<T>>
where
T: TideEntityManagerMeta + Clone + Send + Sync + 'static,
{
if self.cached.is_some() {
if let Some(items) = self.cached.as_ref() {
for entity in items.iter().cloned() {
entity_manager.put(entity);
}
}
self.plain.attach_query_database(entity_manager.database());
self.entity_manager = Some(entity_manager.clone());
let owner_key = self.owner_key.as_deref().ok_or_else(|| {
Error::query(format!(
"entity manager owner key not set for relation '{}'",
self.relation_name
))
})?;
let ids = self.current_keys()?;
entity_manager
.snapshot::<T>(self.owner_table, owner_key, self.relation_name, &ids)
.await;
return Ok(self.cached.as_ref().expect("relation cache should exist"));
}
self.ensure_configured()?;
let pk_value = self
.parent_pk
.as_ref()
.ok_or_else(|| Error::query(String::from("Parent primary key not set for relation")))?;
let pk_value = require_scalar_relation_key(pk_value, "HasMany::load")?;
let owner_key = self.owner_key.as_deref().ok_or_else(|| {
Error::query(format!(
"entity manager owner key not set for relation '{}'",
self.relation_name
))
})?;
let loaded = T::query_with(entity_manager.db.as_ref())
.where_eq(self.foreign_key, pk_value.clone())
.get()
.await?;
let mut registered = Vec::with_capacity(loaded.len());
for entity in loaded {
registered.push(entity_manager.register(entity).await);
}
self.plain.attach_query_database(entity_manager.database());
self.plain.set_cached(registered.clone());
self.cached = Some(registered);
self.entity_manager = Some(entity_manager.clone());
let ids = self.current_keys()?;
entity_manager
.snapshot::<T>(self.owner_table, owner_key, self.relation_name, &ids)
.await;
Ok(self
.cached
.as_ref()
.expect("relation cache should exist after load"))
}
pub async fn load_with<F>(&self, constraint_fn: F) -> Result<Vec<T>>
where
F: FnOnce(crate::query::QueryBuilder<T>) -> crate::query::QueryBuilder<T> + Send,
{
if let Some(entity_manager) = &self.entity_manager {
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, "HasMany::load_with")?;
return constraint_fn(
T::query_with(entity_manager.database()).where_eq(self.foreign_key, pk.clone()),
)
.get()
.await;
}
self.plain.load_with(constraint_fn).await
}
pub async fn count(&self) -> Result<u64> {
if let Some(entity_manager) = &self.entity_manager {
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, "HasMany::count")?;
return T::query_with(entity_manager.database())
.where_eq(self.foreign_key, pk.clone())
.count()
.await;
}
self.plain.count().await
}
pub async fn exists(&self) -> Result<bool> {
if let Some(entity_manager) = &self.entity_manager {
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, "HasMany::exists")?;
return T::query_with(entity_manager.database())
.where_eq(self.foreign_key, pk.clone())
.exists()
.await;
}
self.plain.exists().await
}
}
#[cfg(test)]
#[path = "../../tests/unit/tracked_entity_manager_relation_tests.rs"]
mod tests;
pub trait TrackedHasManyEntityManagerExt<T: Model> {
fn load<'a>(
&'a mut self,
entity_manager: &'a Arc<EntityManager>,
) -> Pin<Box<dyn Future<Output = Result<&'a Vec<T>>> + Send + 'a>>;
}
impl<T> TrackedHasManyEntityManagerExt<T> for TrackedHasMany<T>
where
T: Model + TideEntityManagerMeta + Clone + Send + Sync + 'static,
{
fn load<'a>(
&'a mut self,
entity_manager: &'a Arc<EntityManager>,
) -> Pin<Box<dyn Future<Output = Result<&'a Vec<T>>> + Send + 'a>> {
Box::pin(async move { self.load_in_entity_manager(entity_manager).await })
}
}
impl<T> super::EntityManagerLoad for TrackedHasMany<T>
where
T: Model + TideEntityManagerMeta + Clone + Send + Sync + 'static,
{
type Output<'a>
= &'a Vec<T>
where
Self: 'a;
fn load_with_entity_manager<'a>(
&'a mut self,
entity_manager: &'a Arc<EntityManager>,
) -> Pin<Box<dyn Future<Output = Result<Self::Output<'a>>> + Send + 'a>> {
Box::pin(async move { self.load_in_entity_manager(entity_manager).await })
}
}
impl<T: Model> Deref for TrackedHasMany<T> {
type Target = crate::relations::DirectHasMany<T>;
fn deref(&self) -> &Self::Target {
&self.plain
}
}
impl<T: Model> DerefMut for TrackedHasMany<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.plain
}
}
impl<T: Model + Serialize> Serialize for TrackedHasMany<T> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
self.cached.serialize(serializer)
}
}
impl<'de, T: Model> Deserialize<'de> for TrackedHasMany<T> {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let cached = Option::<Vec<T>>::deserialize(deserializer)?;
let mut relation = Self {
cached,
..Self::default()
};
if let Some(cached) = relation.cached.clone() {
relation.plain.set_cached(cached);
}
Ok(relation)
}
}