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::lens_field, 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) misdeclared: Option<crate::DeclarationErrorKind>,
26}
27
28type BindFn<P> = Arc<dyn Fn(&P) -> (Expr<bool>, String) + Send + Sync>;
30
31pub trait ForeignKey<K>:
34 sealed::Sealed<K> + IntoExpr<Self> + FormScalar + Send + Sync + 'static
35{
36}
37
38impl<K> ForeignKey<K> for K where K: IntoExpr<K> + FormScalar + Send + Sync + 'static {}
39
40impl<K> ForeignKey<K> for Option<K> where
41 Option<K>: IntoExpr<Self> + FormScalar + Send + Sync + 'static
42{
43}
44
45mod sealed {
46 pub trait Sealed<K> {}
47
48 impl<K> Sealed<K> for K {}
49
50 impl<K> Sealed<K> for Option<K> {}
51}
52
53impl<P> Relation<P>
54where
55 P: toasty::schema::Model + IntoExpr<P> + 'static,
56{
57 pub fn has_many<C>(foreign_key: Path<C::Model, impl ForeignKey<P::PrimaryKey>>) -> Self
64 where
65 C: Resource,
66 {
67 Self::bound_by::<C, _>(foreign_key)
68 }
69
70 fn bound_by<C, T>(foreign_key: Path<C::Model, T>) -> Self
71 where
72 C: Resource,
73 T: ForeignKey<P::PrimaryKey>,
74 {
75 let (column, misdeclared) = match lens_field(
77 foreign_key.clone(),
78 &<C::Model as toasty::schema::Model>::schema(),
79 ) {
80 Ok(field) => (field.name.app_unwrap().to_string(), None),
81 Err(error) => (String::new(), Some(error)),
82 };
83 Self {
84 child: TypeId::of::<C>(),
85 child_name: std::any::type_name::<C>(),
86 label: None,
87 foreign_key: column,
88 misdeclared,
89 bind: Arc::new(move |owner| {
90 let scope = foreign_key.clone().eq(pk::pk_expr::<P, T>(owner));
91 (scope, pk::pk_text(owner))
92 }),
93 }
94 }
95}
96
97impl<P> Relation<P> {
98 #[must_use]
100 pub fn label(mut self, label: impl Into<String>) -> Self {
101 self.label = Some(label.into());
102 self
103 }
104
105 pub(crate) fn bind(&self, owner: &P) -> (Expr<bool>, String) {
107 (self.bind)(owner)
108 }
109}
110
111impl<P> std::fmt::Debug for Relation<P> {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 f.debug_struct("Relation")
114 .field("child", &self.child_name)
115 .field("label", &self.label)
116 .field("foreign_key", &self.foreign_key)
117 .finish_non_exhaustive()
118 }
119}