tablo_core/resource/
relation.rs1use std::{any::TypeId, sync::Arc};
4
5use toasty::stmt::{Expr, IntoExpr, Path};
6
7use super::Resource;
8use crate::{form::FormScalar, schema::ResolvedLens, toasty_compat::pk};
9
10pub struct Relation<P> {
19 pub(crate) child: TypeId,
20 pub(crate) child_name: &'static str,
21 pub(crate) label: Option<String>,
22 pub(crate) foreign_key: String,
23 bind: BindFn<P>,
24 pub(crate) scope_of: ScopeFn,
25 pub(crate) misdeclared: Option<crate::DeclarationErrorKind>,
27}
28
29type BindFn<P> = Arc<dyn Fn(&P) -> (Expr<bool>, String) + Send + Sync>;
31
32pub(crate) type ScopeFn = Arc<dyn Fn(&str) -> Option<Expr<bool>> + Send + Sync>;
34
35pub trait ForeignKey<K>:
38 sealed::Sealed<K> + IntoExpr<Self> + FormScalar + Send + Sync + 'static
39{
40}
41
42impl<K> ForeignKey<K> for K where K: IntoExpr<K> + FormScalar + Send + Sync + 'static {}
43
44impl<K> ForeignKey<K> for Option<K> where
45 Option<K>: IntoExpr<Self> + FormScalar + Send + Sync + 'static
46{
47}
48
49mod sealed {
50 pub trait Sealed<K> {}
51
52 impl<K> Sealed<K> for K {}
53
54 impl<K> Sealed<K> for Option<K> {}
55}
56
57impl<P> Relation<P>
58where
59 P: toasty::schema::Model + IntoExpr<P> + 'static,
60{
61 pub fn has_many<C>(foreign_key: Path<C::Model, impl ForeignKey<P::PrimaryKey>>) -> Self
68 where
69 C: Resource,
70 {
71 Self::bound_by::<C, _>(foreign_key)
72 }
73
74 fn bound_by<C, T>(foreign_key: Path<C::Model, T>) -> Self
75 where
76 C: Resource,
77 T: ForeignKey<P::PrimaryKey>,
78 {
79 let binding = ResolvedLens::of(foreign_key.clone());
80 let scope_key = foreign_key.clone();
81 Self {
82 child: TypeId::of::<C>(),
83 child_name: std::any::type_name::<C>(),
84 label: None,
85 foreign_key: binding.name,
86 misdeclared: binding.misdeclared,
87 bind: Arc::new(move |owner| {
88 let scope = foreign_key.clone().eq(pk::pk_expr::<P, T>(owner));
89 (scope, pk::pk_text(owner))
90 }),
91 scope_of: Arc::new(move |owner| {
92 let owner = T::parse_form(owner.trim()).ok()?;
93 Some(scope_key.clone().eq(owner))
94 }),
95 }
96 }
97}
98
99impl<P> Relation<P> {
100 #[must_use]
102 pub fn label(mut self, label: impl Into<String>) -> Self {
103 self.label = Some(label.into());
104 self
105 }
106
107 pub(crate) fn bind(&self, owner: &P) -> (Expr<bool>, String) {
109 (self.bind)(owner)
110 }
111}
112
113impl<P> std::fmt::Debug for Relation<P> {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 f.debug_struct("Relation")
116 .field("child", &self.child_name)
117 .field("label", &self.label)
118 .field("foreign_key", &self.foreign_key)
119 .finish_non_exhaustive()
120 }
121}