1use ciborium::Value as CborValue;
4use indexmap::IndexMap;
5use std::sync::Arc;
6
7use vantage_core::{Result, error};
8use vantage_dataset::WritableValueSet;
9use vantage_expressions::Expression;
10use vantage_types::{EmptyEntity, Entity, Record};
11
12use crate::{
13 column::flags::ColumnFlag,
14 references::{ContainedRelation, HasMany, HasOne, Reference},
15 table::Table,
16 traits::{column_like::ColumnLike, table_source::TableSource},
17};
18
19impl<T: TableSource + 'static, E: Entity<T::Value> + 'static> Table<T, E> {
20 pub fn with_one<E2: Entity<T::Value> + 'static>(
26 mut self,
27 relation: &str,
28 foreign_key: &str,
29 build_target: impl Fn(T) -> Table<T, E2> + Send + Sync + 'static,
30 ) -> Self
31 where
32 T::Value: Into<ciborium::Value> + From<ciborium::Value>,
33 T::Id: std::fmt::Display + From<String>,
34 {
35 let reference = HasOne::<T, E, E2>::new(foreign_key, build_target);
36 self.add_ref(relation, Box::new(reference));
37 self
38 }
39
40 pub fn with_many<E2: Entity<T::Value> + 'static>(
46 mut self,
47 relation: &str,
48 foreign_key: &str,
49 build_target: impl Fn(T) -> Table<T, E2> + Send + Sync + 'static,
50 ) -> Self
51 where
52 T::Value: Into<ciborium::Value> + From<ciborium::Value>,
53 T::Id: std::fmt::Display + From<String>,
54 {
55 let reference = HasMany::<T, E, E2>::new(foreign_key, build_target);
56 self.add_ref(relation, Box::new(reference));
57 self
58 }
59
60 pub fn with_contained_one(
71 mut self,
72 relation: &str,
73 host_column: &str,
74 build_target: impl Fn(T) -> Table<T, EmptyEntity> + Send + Sync + 'static,
75 ) -> Self {
76 self.contained.push(ContainedRelation::new(
77 relation,
78 host_column,
79 vantage_vista::ContainedKind::ContainsOne,
80 None,
81 build_target,
82 ));
83 self
84 }
85
86 pub fn with_contained_many(
99 mut self,
100 relation: &str,
101 host_column: &str,
102 build_target: impl Fn(T) -> Table<T, EmptyEntity> + Send + Sync + 'static,
103 id_column: Option<&str>,
104 ) -> Self {
105 self.contained.push(ContainedRelation::new(
106 relation,
107 host_column,
108 vantage_vista::ContainedKind::ContainsMany,
109 id_column.map(str::to_string),
110 build_target,
111 ));
112 self
113 }
114
115 pub fn vista_columns(&self) -> Vec<vantage_vista::Column>
118 where
119 T::Column<T::AnyType>: ColumnLike<T::AnyType>,
120 {
121 self.columns()
122 .iter()
123 .map(|(name, col)| {
124 let mut vc = vantage_vista::Column::new(name.clone(), col.get_type().to_string());
125 if col.flags().contains(&ColumnFlag::Hidden) {
126 vc = vc.hidden();
127 }
128 vc
129 })
130 .collect()
131 }
132
133 #[allow(clippy::too_many_arguments)]
146 pub fn get_contained_ref(
147 &self,
148 relation: &str,
149 row: &Record<CborValue>,
150 parent_id: T::Id,
151 wrap: impl Fn(Table<T, EmptyEntity>) -> Result<vantage_vista::Vista> + Send + Sync + 'static,
152 decode_host: impl Fn(&CborValue) -> Option<CborValue>,
153 encode_host: impl Fn(CborValue) -> CborValue + Send + Sync + 'static,
154 ) -> Result<vantage_vista::Vista>
155 where
156 T::Value: From<CborValue> + Send + Sync + vantage_types::InvariantValue,
157 T::Id: Clone + Send + Sync,
158 T::Column<T::AnyType>: ColumnLike<T::AnyType>,
159 {
160 let rel = self
161 .contained_relation(relation)
162 .ok_or_else(|| error!("unknown contained relation", relation = relation))?;
163 let host_value = row.get(rel.host_column()).and_then(decode_host);
164
165 let contained_table = rel.build_target(self.data_source().clone());
166 let mut spec = vantage_vista::ContainedSpec::new(rel.name(), rel.host_column(), rel.kind());
167 if let Some(id) = rel.id_column() {
168 spec = spec.with_id_column(id);
169 }
170 spec = spec.with_columns(contained_table.vista_columns());
171
172 let host_column = rel.host_column().to_string();
173 let parent_table = self.clone();
174 let writeback: vantage_vista::ContainedWriteback =
175 Arc::new(move |collection: CborValue| {
176 let parent_table = parent_table.clone();
177 let host_column = host_column.clone();
178 let parent_id = parent_id.clone();
179 let value = T::Value::from(encode_host(collection));
180 Box::pin(async move {
181 let mut patch: Record<T::Value> = Record::new();
182 patch.insert(host_column, value);
183 parent_table.patch_value(parent_id.clone(), &patch).await?;
184 Ok(())
185 })
186 });
187
188 let ref_resolver: vantage_vista::ContainedRefResolver =
189 Arc::new(move |relation: &str, child_row: &Record<CborValue>| {
190 let native: Record<T::Value> = child_row
191 .iter()
192 .map(|(k, v)| (k.clone(), T::Value::from(v.clone())))
193 .collect();
194 let target = contained_table.get_ref_from_row::<EmptyEntity>(relation, &native)?;
195 wrap(target)
196 });
197
198 vantage_vista::build_contained_vista(
199 &spec,
200 host_value.as_ref(),
201 writeback,
202 Some(ref_resolver),
203 )
204 }
205
206 pub fn with_contained_specs<C>(
211 mut self,
212 specs: &IndexMap<String, vantage_vista::ContainedYaml<C>>,
213 build_col: impl Fn(&str, &vantage_vista::ColumnSpec<C>) -> Result<T::Column<T::AnyType>>,
214 ) -> Result<Self>
215 where
216 T::Column<T::AnyType>: Clone,
217 {
218 for (relation, c) in specs {
219 let cols = c
220 .columns
221 .iter()
222 .map(|(n, cs)| build_col(n, cs))
223 .collect::<Result<Vec<_>>>()?;
224 let rel = relation.clone();
225 let host = c.host_column.clone();
226 let build = move |db: T| {
227 let mut t = Table::<T, EmptyEntity>::new(rel.clone(), db);
228 for col in &cols {
229 t.add_column(col.clone());
230 }
231 t
232 };
233 self = match c.kind {
234 vantage_vista::ContainedKind::ContainsOne => {
235 self.with_contained_one(relation, &host, build)
236 }
237 vantage_vista::ContainedKind::ContainsMany => {
238 self.with_contained_many(relation, &host, build, c.id_column.as_deref())
239 }
240 };
241 }
242 Ok(self)
243 }
244
245 pub(crate) fn add_ref(&mut self, relation: &str, reference: Box<dyn Reference>) {
246 self.add_ref_arc(relation, Arc::from(reference));
247 }
248
249 pub(crate) fn add_ref_arc(&mut self, relation: &str, reference: Arc<dyn Reference>) {
252 self.refs
253 .get_or_insert_with(IndexMap::new)
254 .insert(relation.to_string(), reference);
255 }
256
257 pub(crate) fn refs_ref(&self) -> Option<&IndexMap<String, Arc<dyn Reference>>> {
259 self.refs.as_ref()
260 }
261
262 pub fn copy_relations_from<E2: Entity<T::Value> + 'static>(
267 &mut self,
268 other: &Table<T, E2>,
269 names: Option<&[&str]>,
270 ) {
271 let Some(refs) = other.refs_ref() else {
272 return;
273 };
274 for (name, reference) in refs {
275 if names.is_some_and(|ns| !ns.contains(&name.as_str())) {
276 continue;
277 }
278 self.add_ref_arc(name, reference.clone());
279 }
280 }
281
282 pub fn references(&self) -> Vec<String> {
283 self.refs
284 .as_ref()
285 .map(|refs| refs.keys().cloned().collect())
286 .unwrap_or_default()
287 }
288
289 pub fn with_id(mut self, id: impl Into<T::Value>) -> Result<Self> {
296 let id_name = self
297 .id_field()
298 .ok_or_else(|| error!("id field not set on table"))?
299 .name()
300 .to_string();
301 let id = id.into();
302 let condition = self
303 .data_source()
304 .eq_value_condition(&id_name, id.clone())?;
305 self.add_condition(condition);
306 self.add_invariant(id_name, id);
308 Ok(self)
309 }
310
311 pub fn get_ref_from_row<E2: Entity<T::Value> + 'static>(
325 &self,
326 relation: &str,
327 row: &Record<T::Value>,
328 ) -> Result<Table<T, E2>> {
329 let (reference, _) = self.lookup_ref(relation)?;
330 let source_id = self
331 .id_field()
332 .map(|c| c.name().to_string())
333 .unwrap_or_else(|| "id".to_string());
334
335 let target_dyn = reference.resolve_from_row(
336 self.data_source() as &dyn std::any::Any,
337 &source_id,
338 row as &dyn std::any::Any,
339 )?;
340
341 let target_empty: Table<T, EmptyEntity> =
342 *target_dyn
343 .downcast::<Table<T, EmptyEntity>>()
344 .map_err(|_| error!("Failed to downcast target table to Table<T, EmptyEntity>"))?;
345
346 Ok(target_empty.into_entity::<E2>())
347 }
348
349 pub fn get_ref_as<E2: Entity<T::Value> + 'static>(
359 &self,
360 relation: &str,
361 ) -> Result<Table<T, E2>> {
362 let (reference, relation_str) = self.lookup_ref(relation)?;
363
364 let source_id = self
365 .id_field()
366 .map(|c| c.name().to_string())
367 .unwrap_or_else(|| "id".to_string());
368
369 let mut target: Table<T, E2> = *reference
370 .build_target(self.data_source() as &dyn std::any::Any)
371 .downcast::<Table<T, E2>>()
372 .map_err(|_| {
373 error!(
374 "Failed to downcast related table",
375 relation = relation_str.as_str()
376 )
377 })?;
378
379 let target_id = target
380 .id_field()
381 .map(|c| c.name().to_string())
382 .unwrap_or_else(|| "id".to_string());
383
384 let (src_col, tgt_col) = reference.columns(&source_id, &target_id);
385
386 let condition = self
387 .data_source()
388 .related_in_condition(&tgt_col, self, &src_col);
389 target.add_condition(condition);
390
391 Ok(target)
392 }
393
394 pub fn get_subquery_as<E2: Entity<T::Value> + 'static>(
401 &self,
402 relation: &str,
403 ) -> Result<Table<T, E2>> {
404 let (reference, relation_str) = self.lookup_ref(relation)?;
405
406 let source_id = self
407 .id_field()
408 .map(|c| c.name().to_string())
409 .unwrap_or_else(|| "id".to_string());
410
411 let mut target: Table<T, E2> = *reference
412 .build_target(self.data_source() as &dyn std::any::Any)
413 .downcast::<Table<T, E2>>()
414 .map_err(|_| {
415 error!(
416 "Failed to downcast related table",
417 relation = relation_str.as_str()
418 )
419 })?;
420
421 let target_id = target
422 .id_field()
423 .map(|c| c.name().to_string())
424 .unwrap_or_else(|| "id".to_string());
425
426 let (src_col, tgt_col) = reference.columns(&source_id, &target_id);
427
428 let condition = self.data_source().related_correlated_condition(
429 target.table_name(),
430 &tgt_col,
431 self.table_name(),
432 &src_col,
433 );
434 target.add_condition(condition);
435
436 Ok(target)
437 }
438
439 pub fn get_ref_target<E2: Entity<T::Value> + 'static>(
447 &self,
448 relation: &str,
449 ) -> Result<Table<T, E2>> {
450 let (reference, relation_str) = self.lookup_ref(relation)?;
451 let target: Table<T, E2> = *reference
452 .build_target(self.data_source() as &dyn std::any::Any)
453 .downcast::<Table<T, E2>>()
454 .map_err(|_| {
455 error!(
456 "Failed to downcast related table",
457 relation = relation_str.as_str()
458 )
459 })?;
460 Ok(target)
461 }
462
463 pub fn with_expression(
468 mut self,
469 name: &str,
470 expr_fn: impl Fn(&Table<T, E>) -> Expression<T::Value> + Send + Sync + 'static,
471 ) -> Self
472 where
473 T: 'static,
474 E: 'static,
475 {
476 let wrapped: crate::table::base::ExpressionFn<T> =
480 Arc::new(move |erased: &Table<T, vantage_types::EmptyEntity>| {
481 let typed: &Table<T, E> = unsafe {
483 &*(erased as *const Table<T, vantage_types::EmptyEntity> as *const Table<T, E>)
484 };
485 expr_fn(typed)
486 });
487 self.expressions.insert(name.to_string(), wrapped);
488 self
489 }
490
491 fn lookup_ref(&self, relation: &str) -> Result<(&dyn Reference, String)> {
492 let table_name = self.table_name().to_string();
493 let refs = self.refs.as_ref().ok_or_else(|| {
494 error!(
495 "No references defined on table",
496 table = table_name.as_str()
497 )
498 })?;
499
500 let relation_str = relation.to_string();
501 let reference = refs.get(relation).ok_or_else(|| {
502 error!(
503 "Reference not found on table",
504 relation = relation_str.as_str(),
505 table = table_name.as_str()
506 )
507 })?;
508
509 Ok((reference.as_ref(), relation_str))
510 }
511
512 pub fn ref_cardinality(&self, relation: &str) -> Result<vantage_vista::ReferenceKind> {
514 let (reference, _) = self.lookup_ref(relation)?;
515 Ok(reference.cardinality())
516 }
517
518 pub fn ref_kinds(&self) -> Vec<(String, vantage_vista::ReferenceKind)> {
520 self.refs
521 .as_ref()
522 .map(|refs| {
523 refs.iter()
524 .map(|(name, r)| (name.clone(), r.cardinality()))
525 .collect()
526 })
527 .unwrap_or_default()
528 }
529}