vantage_table/references/
many.rs1use std::fmt::Display;
6use std::{any::Any, marker::PhantomData, sync::Arc};
7
8use vantage_core::{Result, error};
9use vantage_types::{EmptyEntity, Entity, Record};
10
11use crate::{references::Reference, table::Table, traits::table_source::TableSource};
12
13pub struct HasMany<T: TableSource, SourceE: Entity<T::Value>, TargetE: Entity<T::Value>> {
14 target_foreign_key: String,
16 build_target: Arc<dyn Fn(T) -> Table<T, TargetE> + Send + Sync>,
18 _phantom: PhantomData<SourceE>,
19}
20
21impl<T: TableSource, SourceE: Entity<T::Value>, TargetE: Entity<T::Value>>
22 HasMany<T, SourceE, TargetE>
23{
24 pub fn new(
25 target_foreign_key: impl Into<String>,
26 build_target: impl Fn(T) -> Table<T, TargetE> + Send + Sync + 'static,
27 ) -> Self {
28 Self {
29 target_foreign_key: target_foreign_key.into(),
30 build_target: Arc::new(build_target),
31 _phantom: PhantomData,
32 }
33 }
34}
35
36impl<T: TableSource, SourceE: Entity<T::Value>, TargetE: Entity<T::Value>> Clone
37 for HasMany<T, SourceE, TargetE>
38{
39 fn clone(&self) -> Self {
40 Self {
41 target_foreign_key: self.target_foreign_key.clone(),
42 build_target: self.build_target.clone(),
43 _phantom: PhantomData,
44 }
45 }
46}
47
48impl<T: TableSource, SourceE: Entity<T::Value>, TargetE: Entity<T::Value>> std::fmt::Debug
49 for HasMany<T, SourceE, TargetE>
50{
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 f.debug_struct("HasMany")
53 .field("target_foreign_key", &self.target_foreign_key)
54 .finish()
55 }
56}
57
58impl<
59 T: TableSource + 'static,
60 SourceE: Entity<T::Value> + 'static,
61 TargetE: Entity<T::Value> + 'static,
62> Reference for HasMany<T, SourceE, TargetE>
63where
64 T::Value: Into<ciborium::Value> + From<ciborium::Value>,
65 T::Id: Display + From<String>,
66{
67 fn columns(&self, source_id: &str, _target_id: &str) -> (String, String) {
68 (source_id.to_string(), self.target_foreign_key.clone())
69 }
70
71 fn foreign_key(&self) -> &str {
72 &self.target_foreign_key
73 }
74
75 fn build_target(&self, data_source: &dyn Any) -> Box<dyn Any> {
76 let ds = data_source
77 .downcast_ref::<T>()
78 .expect("data source type mismatch in HasMany::build_target");
79 Box::new((self.build_target)(ds.clone()))
80 }
81
82 fn build_target_erased(&self, data_source: &dyn Any) -> Box<dyn Any> {
83 let ds = data_source
84 .downcast_ref::<T>()
85 .expect("data source type mismatch in HasMany::build_target_erased");
86 Box::new((self.build_target)(ds.clone()).into_entity::<EmptyEntity>())
87 }
88
89 fn cardinality(&self) -> vantage_vista::ReferenceKind {
90 vantage_vista::ReferenceKind::HasMany
91 }
92
93 fn resolve_from_row(
94 &self,
95 data_source: &dyn Any,
96 source_id_field: &str,
97 source_row: &dyn Any,
98 ) -> Result<Box<dyn Any>> {
99 let ds = data_source
100 .downcast_ref::<T>()
101 .ok_or_else(|| error!("data source type mismatch in HasMany::resolve_from_row"))?;
102 let row = source_row
103 .downcast_ref::<Record<T::Value>>()
104 .ok_or_else(|| error!("source row type mismatch in HasMany::resolve_from_row"))?;
105
106 let mut target = (self.build_target)(ds.clone());
107 let (src_col, tgt_col) = self.columns(source_id_field, "");
108 let join_value = row
109 .get(&src_col)
110 .cloned()
111 .ok_or_else(|| error!("source row missing id field", field = src_col.as_str()))?;
112 let join_value = ds.coerce_reference_value(join_value);
114
115 let condition = ds.eq_value_condition(&tgt_col, join_value.clone())?;
116 target.add_condition(condition);
117 target.add_invariant(tgt_col, join_value);
119
120 Ok(Box::new(target.into_entity::<EmptyEntity>()))
121 }
122
123 fn target_type_name(&self) -> &'static str {
124 std::any::type_name::<Table<T, TargetE>>()
125 }
126}