use std::fmt;
use crate::fields::types::CascadingForeignModelByField;
use crate::internal::djb2;
use crate::internal::field::foreign_model::{ForeignModelField, ForeignModelTrait};
use crate::internal::field::{Field, SingleColumnField};
use crate::internal::query_context::QueryContext;
use crate::prelude::{BackRef, ForeignModelByField};
use crate::{sealed, Model};
pub trait Path: 'static {
sealed!(trait);
type Origin: Model;
type Current: Model;
const IS_ORIGIN: bool = false;
type Step<F>: Path
where
F: Field + PathField<<F as Field>::Type>,
F::ParentField: Field<Model = Self::Current>;
fn add_to_context(context: &mut QueryContext) -> PathId;
fn id(base_path: Option<PathId>) -> PathId;
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct PathId {
hasher: djb2::Hasher,
}
impl PathId {
pub const fn new_origin<M: Model>() -> Self {
let mut hasher = djb2::Hasher::new();
hasher.write(M::TABLE.as_bytes());
Self { hasher }
}
pub const fn add_step<F: Field>(mut self) -> Self {
self.hasher.write(b"\xFF");
self.hasher.write(F::NAME.as_bytes());
self
}
}
impl fmt::Debug for PathId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PathId({:#018x})", self.hasher.0)
}
}
pub trait PathField<FieldType>: Field {
sealed!(trait);
type ChildField: SingleColumnField;
type ParentField: SingleColumnField;
}
impl<M: Model> Path for M {
sealed!(impl);
type Origin = M;
type Current = M;
const IS_ORIGIN: bool = true;
type Step<F>
= (F, Self)
where
F: Field + PathField<<F as Field>::Type>,
F::ParentField: Field<Model = Self::Current>;
#[inline(always)]
fn add_to_context(context: &mut QueryContext) -> PathId {
context.add_origin_path::<Self>()
}
#[inline(always)]
fn id(base_path: Option<PathId>) -> PathId {
base_path.unwrap_or_else(PathId::new_origin::<M>)
}
}
impl<F, P> Path for (F, P)
where
F: Field + PathField<<F as Field>::Type>,
P: Path<Current = <F::ParentField as Field>::Model>,
{
sealed!(impl);
type Origin = P::Origin;
type Current = <<F as PathField<F::Type>>::ChildField as Field>::Model;
type Step<F2>
= (F2, Self)
where
F2: Field + PathField<<F2 as Field>::Type>,
F2::ParentField: Field<Model = Self::Current>;
#[inline(always)]
fn add_to_context(context: &mut QueryContext) -> PathId {
context.add_relation_path::<F, P>()
}
#[inline(always)]
fn id(base_path: Option<PathId>) -> PathId {
P::id(base_path).add_step::<F>()
}
}
impl<FF, F> PathField<ForeignModelByField<FF>> for F
where
FF: SingleColumnField,
F: ForeignModelField<Type = ForeignModelByField<FF>>,
{
sealed!(impl);
type ChildField = FF;
type ParentField = F;
}
impl<FF, F> PathField<Option<ForeignModelByField<FF>>> for F
where
FF: SingleColumnField,
F: ForeignModelField<Type = Option<ForeignModelByField<FF>>>,
{
sealed!(impl);
type ChildField = FF;
type ParentField = F;
}
impl<FF, F> PathField<CascadingForeignModelByField<FF>> for F
where
FF: SingleColumnField,
F: ForeignModelField<Type = CascadingForeignModelByField<FF>>,
{
sealed!(impl);
type ChildField = FF;
type ParentField = F;
}
impl<FF, F> PathField<Option<CascadingForeignModelByField<FF>>> for F
where
FF: SingleColumnField,
F: ForeignModelField<Type = Option<CascadingForeignModelByField<FF>>>,
{
sealed!(impl);
type ChildField = FF;
type ParentField = F;
}
impl<FMF, F> PathField<BackRef<FMF>> for F
where
FMF: ForeignModelField,
F: Field<Type = BackRef<FMF>> + 'static,
{
sealed!(impl);
type ChildField = FMF;
type ParentField = <<FMF as Field>::Type as ForeignModelTrait>::RelatedField;
}