use super::{Field, Load, lazy_slot};
use crate::stmt::{self, Expr, IntoExpr};
use toasty_core::schema::app::ModelSet;
use std::fmt;
#[derive(Clone)]
pub struct Deferred<T> {
value: Option<Box<T>>,
}
impl<T> Deferred<T> {
pub fn is_unloaded(&self) -> bool {
self.value.is_none()
}
#[track_caller]
pub fn get(&self) -> &T {
self.value.as_ref().expect("deferred field not loaded")
}
pub fn unload(&mut self) {
self.value = None;
}
#[track_caller]
pub fn into_inner(self) -> T {
*self.value.expect("deferred field not loaded")
}
}
impl<T> Default for Deferred<T> {
fn default() -> Self {
Self { value: None }
}
}
impl<T> From<T> for Deferred<T> {
fn from(value: T) -> Self {
Self {
value: Some(Box::new(value)),
}
}
}
impl<T: IntoExpr<T>> IntoExpr<T> for Deferred<T> {
fn into_expr(self) -> Expr<T> {
self.into_inner().into_expr()
}
fn by_ref(&self) -> Expr<T> {
self.get().by_ref()
}
}
impl<T: IntoExpr<T>> IntoExpr<T> for &Deferred<T> {
fn into_expr(self) -> Expr<T> {
self.get().by_ref()
}
fn by_ref(&self) -> Expr<T> {
self.get().by_ref()
}
}
impl<T: Load<Output = T>> Load for Deferred<T> {
type Output = Self;
fn ty() -> toasty_core::stmt::Type {
T::ty()
}
fn load(value: toasty_core::stmt::Value) -> crate::Result<Self> {
match lazy_slot::decode(value, "deferred field", T::load)? {
lazy_slot::LazySlot::Unloaded => Ok(Self { value: None }),
lazy_slot::LazySlot::Loaded(value) => Ok(Self {
value: Some(Box::new(value)),
}),
}
}
fn reload(target: &mut Self, value: toasty_core::stmt::Value) -> crate::Result<()> {
target.value = Some(Box::new(T::load(value)?));
Ok(())
}
}
impl<T: Field> Field for Deferred<T> {
type ExprTarget = T::ExprTarget;
type Path<Origin> = T::Path<Origin>;
type ListPath<Origin> = T::ListPath<Origin>;
type Update<'a> = T::Update<'a>;
type Inner = T::Inner;
const NULLABLE: bool = T::NULLABLE;
const DEFERRED: bool = true;
fn new_path<Origin>(path: stmt::Path<Origin, T::ExprTarget>) -> Self::Path<Origin> {
T::new_path(path)
}
fn new_list_path<Origin>(
path: stmt::Path<Origin, stmt::List<Self::ExprTarget>>,
) -> Self::ListPath<Origin> {
T::new_list_path(path)
}
fn new_update<'a>(
assignments: &'a mut toasty_core::stmt::Assignments,
projection: toasty_core::stmt::Projection,
) -> Self::Update<'a> {
T::new_update(assignments, projection)
}
fn field_ty(
storage_ty: Option<toasty_core::schema::db::Type>,
) -> toasty_core::schema::app::FieldTy {
T::field_ty(storage_ty)
}
fn key_constraint<Origin>(&self, _target: stmt::Path<Origin, Self::Inner>) -> Expr<bool> {
unreachable!("Deferred fields cannot be used as foreign keys")
}
fn register(model_set: &mut ModelSet) {
T::register(model_set);
}
}
impl<T: fmt::Debug> fmt::Debug for Deferred<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.value {
Some(value) => value.fmt(f),
None => write!(f, "<not loaded>"),
}
}
}
#[cfg(feature = "serde")]
impl<T: serde_core::Serialize> serde_core::Serialize for Deferred<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde_core::Serializer,
{
self.value.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, T: serde_core::Deserialize<'de>> serde_core::Deserialize<'de> for Deferred<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde_core::Deserializer<'de>,
{
Ok(Self::from(T::deserialize(deserializer)?))
}
}