vantage_table/table/impls/
references.rs1use std::sync::Arc;
4
5use indexmap::IndexMap;
6use vantage_core::{Result, error};
7use vantage_types::Entity;
8
9use crate::{
10 references::{HasMany, HasOne, Reference},
11 table::Table,
12 traits::table_source::TableSource,
13};
14
15mod contained;
16mod traversal;
17
18impl<T: TableSource + 'static, E: Entity<T::Value> + 'static> Table<T, E> {
19 pub fn with_one<E2: Entity<T::Value> + 'static>(
25 mut self,
26 relation: &str,
27 foreign_key: &str,
28 build_target: impl Fn(T) -> Table<T, E2> + Send + Sync + 'static,
29 ) -> Self
30 where
31 T::Value: Into<ciborium::Value> + From<ciborium::Value>,
32 T::Id: std::fmt::Display + From<String>,
33 {
34 let reference = HasOne::<T, E, E2>::new(foreign_key, build_target);
35 self.add_ref(relation, Box::new(reference));
36 self
37 }
38
39 pub fn with_many<E2: Entity<T::Value> + 'static>(
45 mut self,
46 relation: &str,
47 foreign_key: &str,
48 build_target: impl Fn(T) -> Table<T, E2> + Send + Sync + 'static,
49 ) -> Self
50 where
51 T::Value: Into<ciborium::Value> + From<ciborium::Value>,
52 T::Id: std::fmt::Display + From<String>,
53 {
54 let reference = HasMany::<T, E, E2>::new(foreign_key, build_target);
55 self.add_ref(relation, Box::new(reference));
56 self
57 }
58
59 pub fn try_with_one<E2: Entity<T::Value> + 'static>(
63 mut self,
64 relation: &str,
65 foreign_key: &str,
66 target: &str,
67 build_target: impl Fn(T) -> Result<Table<T, E2>> + Send + Sync + 'static,
68 ) -> Self
69 where
70 T::Value: Into<ciborium::Value> + From<ciborium::Value>,
71 T::Id: std::fmt::Display + From<String>,
72 {
73 let reference = HasOne::<T, E, E2>::try_new(foreign_key, target, build_target);
74 self.add_ref(relation, Box::new(reference));
75 self
76 }
77
78 pub fn try_with_many<E2: Entity<T::Value> + 'static>(
80 mut self,
81 relation: &str,
82 foreign_key: &str,
83 target: &str,
84 build_target: impl Fn(T) -> Result<Table<T, E2>> + Send + Sync + 'static,
85 ) -> Self
86 where
87 T::Value: Into<ciborium::Value> + From<ciborium::Value>,
88 T::Id: std::fmt::Display + From<String>,
89 {
90 let reference = HasMany::<T, E, E2>::try_new(foreign_key, target, build_target);
91 self.add_ref(relation, Box::new(reference));
92 self
93 }
94
95 pub(crate) fn add_ref(&mut self, relation: &str, reference: Box<dyn Reference>) {
96 self.add_ref_arc(relation, Arc::from(reference));
97 }
98
99 pub(crate) fn add_ref_arc(&mut self, relation: &str, reference: Arc<dyn Reference>) {
102 self.refs
103 .get_or_insert_with(IndexMap::new)
104 .insert(relation.to_string(), reference);
105 }
106
107 pub fn copy_relations_from<E2: Entity<T::Value> + 'static>(
112 &mut self,
113 other: &Table<T, E2>,
114 names: Option<&[&str]>,
115 ) {
116 let Some(refs) = &other.refs else {
117 return;
118 };
119 for (name, reference) in refs {
120 if names.is_some_and(|ns| !ns.contains(&name.as_str())) {
121 continue;
122 }
123 self.add_ref_arc(name, reference.clone());
124 }
125 }
126
127 pub fn references(&self) -> Vec<String> {
128 self.refs
129 .as_ref()
130 .map(|refs| refs.keys().cloned().collect())
131 .unwrap_or_default()
132 }
133
134 fn lookup_ref(&self, relation: &str) -> Result<&dyn Reference> {
135 let table_name = self.table_name();
136 let refs = self
137 .refs
138 .as_ref()
139 .ok_or_else(|| error!("No references defined on table", table = table_name))?;
140
141 let reference = refs.get(relation).ok_or_else(|| {
142 error!(
143 "Reference not found on table",
144 relation = relation,
145 table = table_name
146 )
147 })?;
148
149 Ok(reference.as_ref())
150 }
151
152 fn ref_shape(&self, relation: &str) -> Result<vantage_vista::Reference> {
157 let reference = self.lookup_ref(relation)?;
158 Ok(reference.vista_reference(relation, String::new(), &self.id_field_name()))
159 }
160
161 pub fn ref_cardinality(&self, relation: &str) -> Result<vantage_vista::ReferenceKind> {
163 Ok(self.ref_shape(relation)?.kind)
164 }
165
166 pub fn ref_foreign_key(&self, relation: &str) -> Result<String> {
172 let shape = self.ref_shape(relation)?;
173 let key = shape
174 .keys
175 .first()
176 .ok_or_else(|| error!("Reference has no join key", relation = relation))?;
177 Ok(match shape.kind {
178 vantage_vista::ReferenceKind::HasMany => key.to.clone().unwrap_or_default(),
179 _ => key.from.clone(),
180 })
181 }
182
183 pub fn ref_kinds(&self) -> Vec<(String, vantage_vista::ReferenceKind)> {
185 let Some(refs) = &self.refs else {
186 return Vec::new();
187 };
188 let source_id = self.id_field_name();
189 refs.iter()
190 .map(|(name, r)| {
191 let kind = r.vista_reference(name, String::new(), &source_id).kind;
192 (name.clone(), kind)
193 })
194 .collect()
195 }
196}