Skip to main content

vantage_table/references/
contained.rs

1//! Contained relation registration — a closure that builds the embedded
2//! record's table, mirroring how [`HasOne`](super::HasOne) /
3//! [`HasMany`](super::HasMany) carry a `build_target` for joined tables.
4//!
5//! The contained record's fields are typed in the parent's own type system (a
6//! line's `product` is the driver's `Thing`, its `quantity` an `i64`), so the
7//! closure has the same shape as `with_many`'s — `Fn(T) -> Table<T, E2>`. It's
8//! evaluated lazily at traversal time; the driver runs its normal
9//! column-harvesting routine on the result to derive the sub-Vista's schema.
10//! The returned table carries a real data source it is never queried through —
11//! only its column/relation schema is read; row data comes from the parent's
12//! embedded column.
13
14use std::sync::Arc;
15
16use vantage_types::EmptyEntity;
17use vantage_vista::{Reference, ReferenceKind};
18
19use crate::{table::Table, traits::table_source::TableSource};
20
21/// A contained (embedded-in-row) relation declared on a [`Table`].
22pub struct ContainedRelation<T: TableSource> {
23    name: String,
24    host_column: String,
25    /// `ContainsOne` or `ContainsMany`: only [`one`](ContainedRelation::one)
26    /// and [`many`](ContainedRelation::many) set it.
27    kind: ReferenceKind,
28    id_column: Option<String>,
29    build_target: Arc<dyn Fn(T) -> Table<T, EmptyEntity> + Send + Sync>,
30}
31
32impl<T: TableSource> Clone for ContainedRelation<T> {
33    fn clone(&self) -> Self {
34        Self {
35            name: self.name.clone(),
36            host_column: self.host_column.clone(),
37            kind: self.kind,
38            id_column: self.id_column.clone(),
39            build_target: self.build_target.clone(),
40        }
41    }
42}
43
44impl<T: TableSource + 'static> ContainedRelation<T> {
45    /// One record embedded as an object in `host_column`; its id is `"0"`.
46    pub fn one(
47        name: impl Into<String>,
48        host_column: impl Into<String>,
49        build_target: impl Fn(T) -> Table<T, EmptyEntity> + Send + Sync + 'static,
50    ) -> Self {
51        Self {
52            name: name.into(),
53            host_column: host_column.into(),
54            kind: ReferenceKind::ContainsOne,
55            id_column: None,
56            build_target: Arc::new(build_target),
57        }
58    }
59
60    /// Records embedded as an array in `host_column`, each addressed by its
61    /// `id_column` field (`None`: by position).
62    pub fn many(
63        name: impl Into<String>,
64        host_column: impl Into<String>,
65        id_column: Option<String>,
66        build_target: impl Fn(T) -> Table<T, EmptyEntity> + Send + Sync + 'static,
67    ) -> Self {
68        Self {
69            name: name.into(),
70            host_column: host_column.into(),
71            kind: ReferenceKind::ContainsMany,
72            id_column,
73            build_target: Arc::new(build_target),
74        }
75    }
76
77    pub fn name(&self) -> &str {
78        &self.name
79    }
80
81    pub fn host_column(&self) -> &str {
82        &self.host_column
83    }
84
85    pub fn kind(&self) -> ReferenceKind {
86        self.kind
87    }
88
89    pub fn id_column(&self) -> Option<&str> {
90        self.id_column.as_deref()
91    }
92
93    /// Build the contained record's table (its schema), used at traversal to
94    /// derive the sub-Vista's columns.
95    pub fn build_target(&self, db: T) -> Table<T, EmptyEntity> {
96        (self.build_target)(db)
97    }
98
99    /// The relation as a Vista reference of a contained kind, for metadata.
100    /// The records' columns and id column are materialized at traversal from
101    /// `build_target`.
102    pub fn reference(&self) -> Reference {
103        if self.kind == ReferenceKind::ContainsMany {
104            Reference::contains_many(&self.name, &self.host_column)
105        } else {
106            Reference::contains_one(&self.name, &self.host_column)
107        }
108    }
109}