use std::sync::Arc;
use indexmap::IndexMap;
use vantage_core::{Result, error};
use vantage_types::Entity;
use crate::{
references::{HasMany, HasOne, Reference},
table::Table,
traits::table_source::TableSource,
};
mod contained;
mod traversal;
impl<T: TableSource + 'static, E: Entity<T::Value> + 'static> Table<T, E> {
pub fn with_one<E2: Entity<T::Value> + 'static>(
mut self,
relation: &str,
foreign_key: &str,
build_target: impl Fn(T) -> Table<T, E2> + Send + Sync + 'static,
) -> Self
where
T::Value: Into<ciborium::Value> + From<ciborium::Value>,
T::Id: std::fmt::Display + From<String>,
{
let reference = HasOne::<T, E, E2>::new(foreign_key, build_target);
self.add_ref(relation, Box::new(reference));
self
}
pub fn with_many<E2: Entity<T::Value> + 'static>(
mut self,
relation: &str,
foreign_key: &str,
build_target: impl Fn(T) -> Table<T, E2> + Send + Sync + 'static,
) -> Self
where
T::Value: Into<ciborium::Value> + From<ciborium::Value>,
T::Id: std::fmt::Display + From<String>,
{
let reference = HasMany::<T, E, E2>::new(foreign_key, build_target);
self.add_ref(relation, Box::new(reference));
self
}
pub(crate) fn add_ref(&mut self, relation: &str, reference: Box<dyn Reference>) {
self.add_ref_arc(relation, Arc::from(reference));
}
pub(crate) fn add_ref_arc(&mut self, relation: &str, reference: Arc<dyn Reference>) {
self.refs
.get_or_insert_with(IndexMap::new)
.insert(relation.to_string(), reference);
}
pub fn copy_relations_from<E2: Entity<T::Value> + 'static>(
&mut self,
other: &Table<T, E2>,
names: Option<&[&str]>,
) {
let Some(refs) = &other.refs else {
return;
};
for (name, reference) in refs {
if names.is_some_and(|ns| !ns.contains(&name.as_str())) {
continue;
}
self.add_ref_arc(name, reference.clone());
}
}
pub fn references(&self) -> Vec<String> {
self.refs
.as_ref()
.map(|refs| refs.keys().cloned().collect())
.unwrap_or_default()
}
fn lookup_ref(&self, relation: &str) -> Result<&dyn Reference> {
let table_name = self.table_name();
let refs = self
.refs
.as_ref()
.ok_or_else(|| error!("No references defined on table", table = table_name))?;
let reference = refs.get(relation).ok_or_else(|| {
error!(
"Reference not found on table",
relation = relation,
table = table_name
)
})?;
Ok(reference.as_ref())
}
fn ref_shape(&self, relation: &str) -> Result<vantage_vista::Reference> {
let reference = self.lookup_ref(relation)?;
Ok(reference.vista_reference(relation, String::new(), &self.id_field_name()))
}
pub fn ref_cardinality(&self, relation: &str) -> Result<vantage_vista::ReferenceKind> {
Ok(self.ref_shape(relation)?.kind)
}
pub fn ref_foreign_key(&self, relation: &str) -> Result<String> {
let shape = self.ref_shape(relation)?;
let key = shape
.keys
.first()
.ok_or_else(|| error!("Reference has no join key", relation = relation))?;
Ok(match shape.kind {
vantage_vista::ReferenceKind::HasMany => key.to.clone().unwrap_or_default(),
_ => key.from.clone(),
})
}
pub fn ref_kinds(&self) -> Vec<(String, vantage_vista::ReferenceKind)> {
let Some(refs) = &self.refs else {
return Vec::new();
};
let source_id = self.id_field_name();
refs.iter()
.map(|(name, r)| {
let kind = r.vista_reference(name, String::new(), &source_id).kind;
(name.clone(), kind)
})
.collect()
}
}