vantage_table/table/impls/
refereces.rs1use indexmap::IndexMap;
4use std::sync::Arc;
5
6use vantage_core::{Result, error};
7use vantage_expressions::Expression;
8use vantage_types::Entity;
9
10use crate::{
11 any::AnyTable,
12 references::{HasForeign, HasMany, HasOne, Reference},
13 table::Table,
14 traits::{column_like::ColumnLike, table_source::TableSource},
15};
16
17impl<T: TableSource + 'static, E: Entity<T::Value> + 'static> Table<T, E> {
18 pub fn with_one<E2: Entity<T::Value> + 'static>(
24 mut self,
25 relation: &str,
26 foreign_key: &str,
27 build_target: impl Fn(T) -> Table<T, E2> + Send + Sync + 'static,
28 ) -> Self
29 where
30 T::Value: Into<ciborium::Value> + From<ciborium::Value>,
31 T::Id: std::fmt::Display + From<String>,
32 {
33 let reference = HasOne::<T, E, E2>::new(foreign_key, build_target);
34 self.add_ref(relation, Box::new(reference));
35 self
36 }
37
38 pub fn with_many<E2: Entity<T::Value> + 'static>(
44 mut self,
45 relation: &str,
46 foreign_key: &str,
47 build_target: impl Fn(T) -> Table<T, E2> + Send + Sync + 'static,
48 ) -> Self
49 where
50 T::Value: Into<ciborium::Value> + From<ciborium::Value>,
51 T::Id: std::fmt::Display + From<String>,
52 {
53 let reference = HasMany::<T, E, E2>::new(foreign_key, build_target);
54 self.add_ref(relation, Box::new(reference));
55 self
56 }
57
58 pub fn with_foreign(
71 mut self,
72 relation: &str,
73 target_type: &'static str,
74 resolve: impl Fn(&Table<T, E>) -> Result<AnyTable> + Send + Sync + 'static,
75 ) -> Self {
76 let reference = HasForeign::<T, E>::new(target_type, resolve);
77 self.add_ref(relation, Box::new(reference));
78 self
79 }
80
81 pub(crate) fn add_ref(&mut self, relation: &str, reference: Box<dyn Reference>) {
82 if self.refs.is_none() {
83 self.refs = Some(IndexMap::new());
84 }
85 self.refs
86 .as_mut()
87 .unwrap()
88 .insert(relation.to_string(), Arc::from(reference));
89 }
90
91 pub fn references(&self) -> Vec<String> {
92 self.refs
93 .as_ref()
94 .map(|refs| refs.keys().cloned().collect())
95 .unwrap_or_default()
96 }
97
98 pub fn is_foreign_ref(&self, relation: &str) -> Result<bool> {
100 let (reference, _) = self.lookup_ref(relation)?;
101 Ok(reference.is_foreign())
102 }
103
104 pub fn get_ref_as<E2: Entity<T::Value> + 'static>(
108 &self,
109 relation: &str,
110 ) -> Result<Table<T, E2>> {
111 let (reference, relation_str) = self.lookup_ref(relation)?;
112
113 if reference.is_foreign() {
114 return Err(error!(
115 "Cannot use get_ref_as for foreign references, use get_ref instead",
116 relation = relation_str.as_str()
117 ));
118 }
119
120 let source_id = self
122 .id_field()
123 .map(|c| c.name().to_string())
124 .unwrap_or_else(|| "id".to_string());
125
126 let mut target: Table<T, E2> = *reference
127 .build_target(self.data_source() as &dyn std::any::Any)
128 .downcast::<Table<T, E2>>()
129 .map_err(|_| {
130 error!(
131 "Failed to downcast related table",
132 relation = relation_str.as_str()
133 )
134 })?;
135
136 let target_id = target
138 .id_field()
139 .map(|c| c.name().to_string())
140 .unwrap_or_else(|| "id".to_string());
141
142 let (src_col, tgt_col) = reference.columns(&source_id, &target_id);
143
144 let condition = self
146 .data_source()
147 .related_in_condition(&tgt_col, self, &src_col);
148 target.add_condition(condition);
149
150 Ok(target)
151 }
152
153 pub fn get_ref(&self, relation: &str) -> Result<AnyTable> {
155 let (reference, _) = self.lookup_ref(relation)?;
156 reference.resolve_as_any(self as &dyn std::any::Any)
157 }
158
159 pub fn get_subquery_as<E2: Entity<T::Value> + 'static>(
165 &self,
166 relation: &str,
167 ) -> Result<Table<T, E2>> {
168 let (reference, relation_str) = self.lookup_ref(relation)?;
169
170 if reference.is_foreign() {
171 return Err(error!(
172 "Cannot use get_subquery_as for foreign references",
173 relation = relation_str.as_str()
174 ));
175 }
176
177 let source_id = self
179 .id_field()
180 .map(|c| c.name().to_string())
181 .unwrap_or_else(|| "id".to_string());
182
183 let mut target: Table<T, E2> = *reference
184 .build_target(self.data_source() as &dyn std::any::Any)
185 .downcast::<Table<T, E2>>()
186 .map_err(|_| {
187 error!(
188 "Failed to downcast related table",
189 relation = relation_str.as_str()
190 )
191 })?;
192
193 let target_id = target
195 .id_field()
196 .map(|c| c.name().to_string())
197 .unwrap_or_else(|| "id".to_string());
198
199 let (src_col, tgt_col) = reference.columns(&source_id, &target_id);
200
201 let condition = self.data_source().related_correlated_condition(
203 target.table_name(),
204 &tgt_col,
205 self.table_name(),
206 &src_col,
207 );
208 target.add_condition(condition);
209
210 Ok(target)
211 }
212
213 pub fn with_expression(
224 mut self,
225 name: &str,
226 expr_fn: impl Fn(&Table<T, E>) -> Expression<T::Value> + Send + Sync + 'static,
227 ) -> Self {
228 self.expressions.insert(name.to_string(), Arc::new(expr_fn));
229 self
230 }
231
232 fn lookup_ref(&self, relation: &str) -> Result<(&dyn Reference, String)> {
233 let table_name = self.table_name().to_string();
234 let refs = self.refs.as_ref().ok_or_else(|| {
235 error!(
236 "No references defined on table",
237 table = table_name.as_str()
238 )
239 })?;
240
241 let relation_str = relation.to_string();
242 let reference = refs.get(relation).ok_or_else(|| {
243 error!(
244 "Reference not found on table",
245 relation = relation_str.as_str(),
246 table = table_name.as_str()
247 )
248 })?;
249
250 Ok((reference.as_ref(), relation_str))
251 }
252}