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 references::{HasMany, HasOne, Reference},
12 table::Table,
13 traits::{column_like::ColumnLike, table_source::TableSource},
14};
15
16impl<T: TableSource + 'static, E: Entity<T::Value> + 'static> Table<T, E> {
17 pub fn with_one<E2: Entity<T::Value> + 'static>(
23 mut self,
24 relation: &str,
25 foreign_key: &str,
26 build_target: impl Fn(T) -> Table<T, E2> + Send + Sync + 'static,
27 ) -> Self
28 where
29 T::Value: Into<ciborium::Value> + From<ciborium::Value>,
30 T::Id: std::fmt::Display + From<String>,
31 {
32 let reference = HasOne::<T, E, E2>::new(foreign_key, build_target);
33 self.add_ref(relation, Box::new(reference));
34 self
35 }
36
37 pub fn with_many<E2: Entity<T::Value> + 'static>(
43 mut self,
44 relation: &str,
45 foreign_key: &str,
46 build_target: impl Fn(T) -> Table<T, E2> + Send + Sync + 'static,
47 ) -> Self
48 where
49 T::Value: Into<ciborium::Value> + From<ciborium::Value>,
50 T::Id: std::fmt::Display + From<String>,
51 {
52 let reference = HasMany::<T, E, E2>::new(foreign_key, build_target);
53 self.add_ref(relation, Box::new(reference));
54 self
55 }
56
57 pub(crate) fn add_ref(&mut self, relation: &str, reference: Box<dyn Reference>) {
58 if self.refs.is_none() {
59 self.refs = Some(IndexMap::new());
60 }
61 self.refs
62 .as_mut()
63 .unwrap()
64 .insert(relation.to_string(), Arc::from(reference));
65 }
66
67 pub fn references(&self) -> Vec<String> {
68 self.refs
69 .as_ref()
70 .map(|refs| refs.keys().cloned().collect())
71 .unwrap_or_default()
72 }
73
74 pub fn with_id(mut self, id: impl Into<T::Value>) -> Result<Self> {
81 let id_name = self
82 .id_field()
83 .ok_or_else(|| error!("id field not set on table"))?
84 .name()
85 .to_string();
86 let condition = self.data_source().eq_value_condition(&id_name, id.into())?;
87 self.add_condition(condition);
88 Ok(self)
89 }
90
91 pub fn get_ref_from_row<E2: Entity<T::Value> + 'static>(
105 &self,
106 relation: &str,
107 row: &Record<T::Value>,
108 ) -> Result<Table<T, E2>> {
109 let (reference, _) = self.lookup_ref(relation)?;
110 let source_id = self
111 .id_field()
112 .map(|c| c.name().to_string())
113 .unwrap_or_else(|| "id".to_string());
114
115 let target_dyn = reference.resolve_from_row(
116 self.data_source() as &dyn std::any::Any,
117 &source_id,
118 row as &dyn std::any::Any,
119 )?;
120
121 let target_empty: Table<T, EmptyEntity> =
122 *target_dyn
123 .downcast::<Table<T, EmptyEntity>>()
124 .map_err(|_| error!("Failed to downcast target table to Table<T, EmptyEntity>"))?;
125
126 Ok(target_empty.into_entity::<E2>())
127 }
128
129 pub fn get_ref_as<E2: Entity<T::Value> + 'static>(
139 &self,
140 relation: &str,
141 ) -> Result<Table<T, E2>> {
142 let (reference, relation_str) = self.lookup_ref(relation)?;
143
144 let source_id = self
145 .id_field()
146 .map(|c| c.name().to_string())
147 .unwrap_or_else(|| "id".to_string());
148
149 let mut target: Table<T, E2> = *reference
150 .build_target(self.data_source() as &dyn std::any::Any)
151 .downcast::<Table<T, E2>>()
152 .map_err(|_| {
153 error!(
154 "Failed to downcast related table",
155 relation = relation_str.as_str()
156 )
157 })?;
158
159 let target_id = target
160 .id_field()
161 .map(|c| c.name().to_string())
162 .unwrap_or_else(|| "id".to_string());
163
164 let (src_col, tgt_col) = reference.columns(&source_id, &target_id);
165
166 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_subquery_as<E2: Entity<T::Value> + 'static>(
181 &self,
182 relation: &str,
183 ) -> Result<Table<T, E2>> {
184 let (reference, relation_str) = self.lookup_ref(relation)?;
185
186 let source_id = self
187 .id_field()
188 .map(|c| c.name().to_string())
189 .unwrap_or_else(|| "id".to_string());
190
191 let mut target: Table<T, E2> = *reference
192 .build_target(self.data_source() as &dyn std::any::Any)
193 .downcast::<Table<T, E2>>()
194 .map_err(|_| {
195 error!(
196 "Failed to downcast related table",
197 relation = relation_str.as_str()
198 )
199 })?;
200
201 let target_id = target
202 .id_field()
203 .map(|c| c.name().to_string())
204 .unwrap_or_else(|| "id".to_string());
205
206 let (src_col, tgt_col) = reference.columns(&source_id, &target_id);
207
208 let condition = self.data_source().related_correlated_condition(
209 target.table_name(),
210 &tgt_col,
211 self.table_name(),
212 &src_col,
213 );
214 target.add_condition(condition);
215
216 Ok(target)
217 }
218
219 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
253 pub fn ref_cardinality(&self, relation: &str) -> Result<vantage_vista::ReferenceKind> {
255 let (reference, _) = self.lookup_ref(relation)?;
256 Ok(reference.cardinality())
257 }
258
259 pub fn ref_kinds(&self) -> Vec<(String, vantage_vista::ReferenceKind)> {
261 self.refs
262 .as_ref()
263 .map(|refs| {
264 refs.iter()
265 .map(|(name, r)| (name.clone(), r.cardinality()))
266 .collect()
267 })
268 .unwrap_or_default()
269 }
270}