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