vantage_vista/spec/
lowering.rs1use std::sync::Arc;
4
5use vantage_core::{Result, error};
6
7use super::{ColumnSpec, ReferenceSpec, VistaSpec};
8use crate::column::ColumnFlag;
9use crate::reference::{JoinKey, Reference, ReferenceKind};
10
11pub trait SpecResolver<S>: Send + Sync {
16 fn resolve(&self, name: &str) -> Option<S>;
17}
18
19impl<S, F: Fn(&str) -> Option<S> + Send + Sync> SpecResolver<S> for F {
20 fn resolve(&self, name: &str) -> Option<S> {
21 self(name)
22 }
23}
24
25impl<T, C, R> VistaSpec<T, C, R> {
26 pub fn resolve_id_column(&self, default: &str) -> String {
29 if let Some(id) = &self.id_column {
30 return id.clone();
31 }
32 self.columns
33 .iter()
34 .find(|(_, col)| col.has_flag(ColumnFlag::Id))
35 .map_or_else(|| default.to_string(), |(name, _)| name.clone())
36 }
37}
38
39impl<C> ColumnSpec<C> {
40 pub fn has_flag(&self, flag: impl Into<ColumnFlag>) -> bool {
41 self.flags.contains(&flag.into())
42 }
43}
44
45impl<R> ReferenceSpec<R> {
46 pub fn foreign_key(&self, relation: &str) -> String {
49 self.foreign_key
50 .clone()
51 .unwrap_or_else(|| relation.to_string())
52 }
53
54 pub fn to_reference(&self, relation: &str, source_id: &str) -> Result<Reference> {
61 let mut keys = self.keys.iter().cloned();
62 let first = keys.next();
63 let target = self.table.clone();
64 let join = match self.kind {
65 ReferenceKind::HasOne => Reference::has_one(
66 relation,
67 target,
68 first.unwrap_or_else(|| JoinKey::to_target_id(self.foreign_key(relation))),
69 ),
70 ReferenceKind::HasMany => Reference::has_many(
71 relation,
72 target,
73 first.unwrap_or_else(|| JoinKey::new(source_id, self.foreign_key(relation))),
74 ),
75 _ => {
76 return Err(error!(
77 "references: entries must be has_one or has_many",
78 relation = relation
79 ));
80 }
81 };
82 Ok(keys.fold(join, Reference::with_key))
83 }
84}
85
86pub fn resolve_base_spec<S>(
89 resolver: Option<Arc<dyn SpecResolver<S>>>,
90 base: &str,
91) -> Result<(Arc<dyn SpecResolver<S>>, S)> {
92 let resolver = resolver.ok_or_else(|| {
93 error!(
94 "vista declares `base:` but no spec resolver is attached to the factory",
95 base = base
96 )
97 })?;
98 let spec = resolver
99 .resolve(base)
100 .ok_or_else(|| error!("base vista not found via resolver", base = base))?;
101 Ok((resolver, spec))
102}