vantage_table/table/impls/
spec.rs1use indexmap::IndexMap;
6use vantage_core::{Result, error};
7use vantage_expressions::{Expressive, SelectableDataSource};
8use vantage_types::{EmptyEntity, Entity};
9use vantage_vista::{ColumnSpec, ReferenceKind, ReferenceSpec, SpecResolver, VistaSpec, flags};
10
11use crate::{source::SelectSource, table::Table, traits::table_source::TableSource};
12
13type SpecTableBuilder<S, T> = fn(&S, T, Option<SpecResolver<S>>) -> Result<Table<T, EmptyEntity>>;
15
16impl<T: TableSource, E: Entity<T::Value>> Table<T, E> {
17 pub fn add_spec_column<C>(
22 &mut self,
23 name: &str,
24 spec: &ColumnSpec<C>,
25 build: impl FnOnce(&str, &ColumnSpec<C>) -> Result<T::Column<T::AnyType>>,
26 ) -> Result<bool> {
27 if self.add_lazy_spec_column(spec, name)? {
28 return Ok(false);
29 }
30 if !self.columns.contains_key(name) {
31 self.add_column(build(name, spec)?);
32 }
33 if spec.has_flag(flags::TITLE) {
34 self.add_title_field(name);
35 }
36 Ok(true)
37 }
38
39 pub fn add_spec_columns<C>(
41 &mut self,
42 columns: &IndexMap<String, ColumnSpec<C>>,
43 build: impl Fn(&str, &ColumnSpec<C>) -> Result<T::Column<T::AnyType>>,
44 ) -> Result<()> {
45 for (name, spec) in columns {
46 self.add_spec_column(name, spec, &build)?;
47 }
48 Ok(())
49 }
50
51 pub fn set_spec_id_field(&mut self, id: &str) -> Result<()> {
54 if !self.columns.contains_key(id) {
55 return Err(error!("id column not present in spec.columns", id = id));
56 }
57 self.set_id_field(id);
58 Ok(())
59 }
60}
61
62impl<T: TableSource + 'static, E: Entity<T::Value> + 'static> Table<T, E>
63where
64 T::Value: Into<ciborium::Value> + From<ciborium::Value>,
65 T::Id: std::fmt::Display + From<String>,
66{
67 pub fn with_spec_references<R, S: 'static>(
74 mut self,
75 references: &IndexMap<String, ReferenceSpec<R>>,
76 resolver: Option<SpecResolver<S>>,
77 build: SpecTableBuilder<S, T>,
78 ) -> Self {
79 for (relation, reference) in references {
80 let target = reference.table.clone();
81 let foreign_key = reference.foreign_key_or(relation);
82 let resolver = resolver.clone();
83 let build_target = move |source: T| -> Table<T, EmptyEntity> {
84 if let Some(r) = &resolver
85 && let Some(spec) = r(&target)
86 && let Ok(table) = build(&spec, source.clone(), Some(r.clone()))
87 {
88 return table;
89 }
90 Table::new(target.clone(), source)
91 };
92 self = match reference.kind {
93 ReferenceKind::HasOne => self.with_one(relation, &foreign_key, build_target),
94 ReferenceKind::HasMany => self.with_many(relation, &foreign_key, build_target),
95 };
96 }
97 self
98 }
99}
100
101impl<T, E, V, Cond, S> Table<T, E>
103where
104 T: SelectableDataSource<V, Cond, Select = S>
105 + TableSource<Value = V, Condition = Cond, Source = SelectSource<S>>,
106 V: Clone + Send + Sync + 'static + From<String>,
107 Cond: Clone + Send + Sync + 'static,
108 S: Expressive<V> + Clone,
109 E: Entity<V>,
110{
111 pub fn derive_from_spec<E2: Entity<V> + 'static, X, C, R>(
116 base: &Table<T, E2>,
117 spec: &VistaSpec<X, C, R>,
118 select: S,
119 inherit_columns: &[String],
120 inherit_relations: &[String],
121 build_column: impl Fn(&str, &ColumnSpec<C>) -> Result<T::Column<T::AnyType>>,
122 ) -> Result<Self>
123 where
124 T: 'static,
125 E: 'static,
126 {
127 let columns: Vec<&str> = inherit_columns.iter().map(String::as_str).collect();
128 let relations: Vec<&str> = inherit_relations.iter().map(String::as_str).collect();
129 let mut table = Self::derive_from(
130 base,
131 spec.name.clone(),
132 move |_| select,
133 &columns,
134 &relations,
135 );
136 table.add_spec_columns(&spec.columns, &build_column)?;
137 if let Some(id) = &spec.id_column {
138 table.set_id_field(id);
139 }
140 table.with_contained_specs(&spec.contained, build_column)
141 }
142}