use super::{Field, Load};
use crate::Statement;
use crate::stmt::{self, Expr, IntoExpr, IntoStatement};
use toasty_core::schema::app::ModelSet;
use std::fmt;
pub struct Deferred<T> {
value: Option<T>,
}
#[diagnostic::on_unimplemented(
message = "`#[deferred]` requires the field to be wrapped in `Deferred<T>`",
label = "expected `Deferred<T>`, found `{Self}`"
)]
pub trait Defer {
type Inner;
}
impl<T> Defer for Deferred<T> {
type Inner = 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")
}
}
#[doc(hidden)]
pub fn build_deferred_load<T, S: IntoStatement>(stmt: S, field_index: usize) -> Statement<T> {
let mut untyped = stmt.into_statement().into_untyped();
*untyped.returning_mut_unwrap() = toasty_core::stmt::Returning::Project(
toasty_core::stmt::Expr::Reference(toasty_core::stmt::ExprReference::Field {
nesting: 0,
index: field_index,
}),
);
Statement::from_untyped_stmt(untyped)
}
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(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 value {
toasty_core::stmt::Value::Null => Ok(Self { value: None }),
toasty_core::stmt::Value::Record(record) if record.fields.len() == 1 => {
let mut iter = record.fields.into_iter();
Ok(Self {
value: Some(T::load(iter.next().unwrap())?),
})
}
value => Err(toasty_core::Error::from_args(format_args!(
"deferred field decoder expected Null or single-field Record, got {value:?}"
))),
}
}
fn reload(target: &mut Self, value: toasty_core::stmt::Value) -> crate::Result<()> {
target.value = Some(T::load(value)?);
Ok(())
}
}
impl<T: Field<Output = T>> 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;
fn new_path<Origin>(_path: stmt::Path<Origin, T::ExprTarget>) -> Self::Path<Origin> {
unreachable!("Deferred::new_path should not be called directly")
}
fn new_list_path<Origin>(
_path: stmt::Path<Origin, stmt::List<Self>>,
) -> Self::ListPath<Origin> {
unreachable!("Deferred::new_list_path should not be called directly")
}
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)
}
}