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