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};
10use vantage_vista::{JoinKey, Reference as VistaReference};
11
12use crate::{references::Reference, table::Table, traits::table_source::TableSource};
13
14pub struct HasMany<T: TableSource, SourceE: Entity<T::Value>, TargetE: Entity<T::Value>> {
15 target_foreign_key: String,
17 build_target: Arc<dyn Fn(T) -> Result<Table<T, TargetE>> + Send + Sync>,
19 target_hint: Option<String>,
21 _phantom: PhantomData<SourceE>,
22}
23
24impl<T: TableSource, SourceE: Entity<T::Value>, TargetE: Entity<T::Value>>
25 HasMany<T, SourceE, TargetE>
26{
27 pub fn new(
28 target_foreign_key: impl Into<String>,
29 build_target: impl Fn(T) -> Table<T, TargetE> + Send + Sync + 'static,
30 ) -> Self {
31 Self {
32 target_foreign_key: target_foreign_key.into(),
33 build_target: Arc::new(move |source| Ok(build_target(source))),
34 target_hint: None,
35 _phantom: PhantomData,
36 }
37 }
38
39 pub fn try_new(
42 target_foreign_key: impl Into<String>,
43 target: impl Into<String>,
44 build_target: impl Fn(T) -> Result<Table<T, TargetE>> + Send + Sync + 'static,
45 ) -> Self {
46 Self {
47 target_foreign_key: target_foreign_key.into(),
48 build_target: Arc::new(build_target),
49 target_hint: Some(target.into()),
50 _phantom: PhantomData,
51 }
52 }
53}
54
55impl<T: TableSource, SourceE: Entity<T::Value>, TargetE: Entity<T::Value>> Clone
56 for HasMany<T, SourceE, TargetE>
57{
58 fn clone(&self) -> Self {
59 Self {
60 target_foreign_key: self.target_foreign_key.clone(),
61 build_target: self.build_target.clone(),
62 target_hint: self.target_hint.clone(),
63 _phantom: PhantomData,
64 }
65 }
66}
67
68impl<T: TableSource, SourceE: Entity<T::Value>, TargetE: Entity<T::Value>> std::fmt::Debug
69 for HasMany<T, SourceE, TargetE>
70{
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 f.debug_struct("HasMany")
73 .field("target_foreign_key", &self.target_foreign_key)
74 .finish()
75 }
76}
77
78impl<
79 T: TableSource + 'static,
80 SourceE: Entity<T::Value> + 'static,
81 TargetE: Entity<T::Value> + 'static,
82> Reference for HasMany<T, SourceE, TargetE>
83where
84 T::Value: Into<ciborium::Value> + From<ciborium::Value>,
85 T::Id: Display + From<String>,
86{
87 fn columns(&self, source_id: &str, _target_id: &str) -> (String, String) {
88 (source_id.to_string(), self.target_foreign_key.clone())
89 }
90
91 fn vista_reference(&self, name: &str, target: String, source_id: &str) -> VistaReference {
92 VistaReference::has_many(
93 name,
94 target,
95 JoinKey::new(source_id, &self.target_foreign_key),
96 )
97 }
98
99 fn build_target(&self, data_source: &dyn Any) -> Result<Box<dyn Any>> {
100 let ds = data_source
101 .downcast_ref::<T>()
102 .ok_or_else(|| error!("data source type mismatch in HasMany::build_target"))?;
103 Ok(Box::new((self.build_target)(ds.clone())?))
104 }
105
106 fn build_target_erased(&self, data_source: &dyn Any) -> Result<Box<dyn Any>> {
107 let ds = data_source
108 .downcast_ref::<T>()
109 .ok_or_else(|| error!("data source type mismatch in HasMany::build_target_erased"))?;
110 Ok(Box::new(
111 (self.build_target)(ds.clone())?.into_entity::<EmptyEntity>(),
112 ))
113 }
114
115 fn resolve_from_row(
116 &self,
117 data_source: &dyn Any,
118 source_id_field: &str,
119 source_row: &dyn Any,
120 ) -> Result<Box<dyn Any>> {
121 let ds = data_source
122 .downcast_ref::<T>()
123 .ok_or_else(|| error!("data source type mismatch in HasMany::resolve_from_row"))?;
124 let row = source_row
125 .downcast_ref::<Record<T::Value>>()
126 .ok_or_else(|| error!("source row type mismatch in HasMany::resolve_from_row"))?;
127
128 let mut target = (self.build_target)(ds.clone())?;
129 let (src_col, tgt_col) = self.columns(source_id_field, "");
130 let join_value = row
131 .get(&src_col)
132 .cloned()
133 .ok_or_else(|| error!("source row missing id field", field = src_col.as_str()))?;
134 let join_value = ds.coerce_reference_value(join_value);
136
137 let condition = ds.eq_value_condition(&tgt_col, join_value.clone())?;
138 target.add_condition(condition);
139 target.add_invariant(tgt_col, join_value);
140
141 Ok(Box::new(target.into_entity::<EmptyEntity>()))
142 }
143
144 fn target_table_name(&self, data_source: &dyn Any) -> String {
147 if let Some(hint) = &self.target_hint {
148 return hint.clone();
149 }
150 let ds = data_source
151 .downcast_ref::<T>()
152 .expect("data source type mismatch in HasMany::target_table_name");
153 (self.build_target)(ds.clone())
154 .map(|target| target.table_name().to_string())
155 .unwrap_or_default()
156 }
157}