vantage_table/references/
one.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 HasOne<T: TableSource, SourceE: Entity<T::Value>, TargetE: Entity<T::Value>> {
15 foreign_key: String,
17 build_target: Arc<dyn Fn(T) -> Table<T, TargetE> + Send + Sync>,
19 _phantom: PhantomData<SourceE>,
20}
21
22impl<T: TableSource, SourceE: Entity<T::Value>, TargetE: Entity<T::Value>>
23 HasOne<T, SourceE, TargetE>
24{
25 pub fn new(
26 foreign_key: impl Into<String>,
27 build_target: impl Fn(T) -> Table<T, TargetE> + Send + Sync + 'static,
28 ) -> Self {
29 Self {
30 foreign_key: foreign_key.into(),
31 build_target: Arc::new(build_target),
32 _phantom: PhantomData,
33 }
34 }
35}
36
37impl<T: TableSource, SourceE: Entity<T::Value>, TargetE: Entity<T::Value>> Clone
38 for HasOne<T, SourceE, TargetE>
39{
40 fn clone(&self) -> Self {
41 Self {
42 foreign_key: self.foreign_key.clone(),
43 build_target: self.build_target.clone(),
44 _phantom: PhantomData,
45 }
46 }
47}
48
49impl<T: TableSource, SourceE: Entity<T::Value>, TargetE: Entity<T::Value>> std::fmt::Debug
50 for HasOne<T, SourceE, TargetE>
51{
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 f.debug_struct("HasOne")
54 .field("foreign_key", &self.foreign_key)
55 .finish()
56 }
57}
58
59impl<
60 T: TableSource + 'static,
61 SourceE: Entity<T::Value> + 'static,
62 TargetE: Entity<T::Value> + 'static,
63> Reference for HasOne<T, SourceE, TargetE>
64where
65 T::Value: Into<ciborium::Value> + From<ciborium::Value>,
66 T::Id: Display + From<String>,
67{
68 fn columns(&self, _source_id: &str, target_id: &str) -> (String, String) {
69 (self.foreign_key.clone(), target_id.to_string())
70 }
71
72 fn vista_reference(&self, name: &str, target: String, _source_id: &str) -> VistaReference {
73 VistaReference::has_one(name, target, JoinKey::to_target_id(&self.foreign_key))
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 HasOne::build_target");
80 Box::new((self.build_target)(ds.clone()))
81 }
82
83 fn build_target_erased(&self, data_source: &dyn Any) -> Box<dyn Any> {
84 let ds = data_source
85 .downcast_ref::<T>()
86 .expect("data source type mismatch in HasOne::build_target_erased");
87 Box::new((self.build_target)(ds.clone()).into_entity::<EmptyEntity>())
88 }
89
90 fn resolve_from_row(
91 &self,
92 data_source: &dyn Any,
93 _source_id_field: &str,
94 source_row: &dyn Any,
95 ) -> Result<Box<dyn Any>> {
96 let ds = data_source
97 .downcast_ref::<T>()
98 .ok_or_else(|| error!("data source type mismatch in HasOne::resolve_from_row"))?;
99 let row = source_row
100 .downcast_ref::<Record<T::Value>>()
101 .ok_or_else(|| error!("source row type mismatch in HasOne::resolve_from_row"))?;
102
103 let mut target = (self.build_target)(ds.clone());
104 let target_id = target.id_field_name();
105
106 let (src_col, tgt_col) = self.columns("", &target_id);
107 let join_value = row.get(&src_col).cloned().ok_or_else(|| {
108 error!(
109 "source row missing foreign-key field",
110 field = src_col.as_str()
111 )
112 })?;
113 let join_value = ds.coerce_reference_value(join_value);
115
116 let condition = ds.eq_value_condition(&tgt_col, join_value.clone())?;
117 target.add_condition(condition);
118 target.add_invariant(tgt_col, join_value);
120
121 Ok(Box::new(target.into_entity::<EmptyEntity>()))
122 }
123
124 fn target_table_name(&self, data_source: &dyn Any) -> String {
125 let ds = data_source
126 .downcast_ref::<T>()
127 .expect("data source type mismatch in HasOne::target_table_name");
128 (self.build_target)(ds.clone()).table_name().to_string()
129 }
130}