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::{EmptyEntity, Entity, Record};
9
10use crate::{
11 any::AnyTable,
12 references::{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(crate) fn add_ref(&mut self, relation: &str, reference: Box<dyn Reference>) {
59 if self.refs.is_none() {
60 self.refs = Some(IndexMap::new());
61 }
62 self.refs
63 .as_mut()
64 .unwrap()
65 .insert(relation.to_string(), Arc::from(reference));
66 }
67
68 pub fn references(&self) -> Vec<String> {
69 self.refs
70 .as_ref()
71 .map(|refs| refs.keys().cloned().collect())
72 .unwrap_or_default()
73 }
74
75 pub fn with_id(mut self, id: impl Into<T::Value>) -> Result<Self> {
82 let id_name = self
83 .id_field()
84 .ok_or_else(|| error!("id field not set on table"))?
85 .name()
86 .to_string();
87 let condition = self.data_source().eq_value_condition(&id_name, id.into())?;
88 self.add_condition(condition);
89 Ok(self)
90 }
91
92 pub fn get_ref_from_row<E2: Entity<T::Value> + 'static>(
106 &self,
107 relation: &str,
108 row: &Record<T::Value>,
109 ) -> Result<Table<T, E2>> {
110 let (reference, _) = self.lookup_ref(relation)?;
111 let source_id = self
112 .id_field()
113 .map(|c| c.name().to_string())
114 .unwrap_or_else(|| "id".to_string());
115
116 let target_dyn = reference.resolve_from_row(
117 self.data_source() as &dyn std::any::Any,
118 &source_id,
119 row as &dyn std::any::Any,
120 )?;
121
122 let target_empty: Table<T, EmptyEntity> =
123 *target_dyn
124 .downcast::<Table<T, EmptyEntity>>()
125 .map_err(|_| error!("Failed to downcast target table to Table<T, EmptyEntity>"))?;
126
127 Ok(target_empty.into_entity::<E2>())
128 }
129
130 pub fn get_ref_as<E2: Entity<T::Value> + 'static>(
136 &self,
137 relation: &str,
138 ) -> Result<Table<T, E2>> {
139 let (reference, relation_str) = self.lookup_ref(relation)?;
140
141 let source_id = self
143 .id_field()
144 .map(|c| c.name().to_string())
145 .unwrap_or_else(|| "id".to_string());
146
147 let mut target: Table<T, E2> = *reference
148 .build_target(self.data_source() as &dyn std::any::Any)
149 .downcast::<Table<T, E2>>()
150 .map_err(|_| {
151 error!(
152 "Failed to downcast related table",
153 relation = relation_str.as_str()
154 )
155 })?;
156
157 let target_id = target
159 .id_field()
160 .map(|c| c.name().to_string())
161 .unwrap_or_else(|| "id".to_string());
162
163 let (src_col, tgt_col) = reference.columns(&source_id, &target_id);
164
165 let condition = self
167 .data_source()
168 .related_in_condition(&tgt_col, self, &src_col);
169 target.add_condition(condition);
170
171 Ok(target)
172 }
173
174 pub fn get_ref(&self, relation: &str) -> Result<AnyTable> {
178 let (reference, _) = self.lookup_ref(relation)?;
179 reference.resolve_as_any(self as &dyn std::any::Any)
180 }
181
182 pub fn get_subquery_as<E2: Entity<T::Value> + 'static>(
188 &self,
189 relation: &str,
190 ) -> Result<Table<T, E2>> {
191 let (reference, relation_str) = self.lookup_ref(relation)?;
192
193 let source_id = self
195 .id_field()
196 .map(|c| c.name().to_string())
197 .unwrap_or_else(|| "id".to_string());
198
199 let mut target: Table<T, E2> = *reference
200 .build_target(self.data_source() as &dyn std::any::Any)
201 .downcast::<Table<T, E2>>()
202 .map_err(|_| {
203 error!(
204 "Failed to downcast related table",
205 relation = relation_str.as_str()
206 )
207 })?;
208
209 let target_id = target
211 .id_field()
212 .map(|c| c.name().to_string())
213 .unwrap_or_else(|| "id".to_string());
214
215 let (src_col, tgt_col) = reference.columns(&source_id, &target_id);
216
217 let condition = self.data_source().related_correlated_condition(
219 target.table_name(),
220 &tgt_col,
221 self.table_name(),
222 &src_col,
223 );
224 target.add_condition(condition);
225
226 Ok(target)
227 }
228
229 pub fn with_expression(
240 mut self,
241 name: &str,
242 expr_fn: impl Fn(&Table<T, E>) -> Expression<T::Value> + Send + Sync + 'static,
243 ) -> Self {
244 self.expressions.insert(name.to_string(), Arc::new(expr_fn));
245 self
246 }
247
248 fn lookup_ref(&self, relation: &str) -> Result<(&dyn Reference, String)> {
249 let table_name = self.table_name().to_string();
250 let refs = self.refs.as_ref().ok_or_else(|| {
251 error!(
252 "No references defined on table",
253 table = table_name.as_str()
254 )
255 })?;
256
257 let relation_str = relation.to_string();
258 let reference = refs.get(relation).ok_or_else(|| {
259 error!(
260 "Reference not found on table",
261 relation = relation_str.as_str(),
262 table = table_name.as_str()
263 )
264 })?;
265
266 Ok((reference.as_ref(), relation_str))
267 }
268
269 pub fn ref_cardinality(&self, relation: &str) -> Result<vantage_vista::ReferenceKind> {
271 let (reference, _) = self.lookup_ref(relation)?;
272 Ok(reference.cardinality())
273 }
274
275 pub fn ref_kinds(&self) -> Vec<(String, vantage_vista::ReferenceKind)> {
277 self.refs
278 .as_ref()
279 .map(|refs| {
280 refs.iter()
281 .map(|(name, r)| (name.clone(), r.cardinality()))
282 .collect()
283 })
284 .unwrap_or_default()
285 }
286}