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