Skip to main content

vantage_table/
references.rs

1//! Table reference system for relationships between tables.
2//!
3//! The `Reference` trait describes a relationship (field names, target factory).
4//! Same-persistence resolution happens in `Table::get_ref_from_row` via
5//! `Reference::resolve_from_row` — the join value is read out of a known source
6//! row and pushed as a plain eq-condition on the target.
7//!
8//! Two concrete types:
9//! - `HasOne` — foreign key on source table (e.g. Client.bakery_id → Bakery)
10//! - `HasMany` — foreign key on target table (e.g. Bakery → Client.bakery_id)
11//!
12//! Cross-persistence references go through a `vantage_vista::Catalog`,
13//! not here. Typed `Table<T, E>` is single-backend by construction.
14
15use std::any::Any;
16
17use vantage_core::Result;
18
19pub mod contained;
20pub mod many;
21pub mod one;
22
23pub use contained::ContainedRelation;
24pub use many::HasMany;
25pub use one::HasOne;
26
27/// Describes a relationship between two tables.
28pub trait Reference: Send + Sync {
29    /// Given source and target id field names, return (source_column, target_column).
30    fn columns(&self, source_id: &str, target_id: &str) -> (String, String);
31
32    /// This relation as Vista metadata: a join named `name` onto `target`.
33    /// `source_id` is the source column a `has_many` reads its join value
34    /// from; a `has_one` ignores it and constrains the target's id column,
35    /// resolved at traversal. Only joins implement this trait, so the result
36    /// is never a contained kind.
37    fn vista_reference(
38        &self,
39        name: &str,
40        target: String,
41        source_id: &str,
42    ) -> vantage_vista::Reference;
43
44    /// Produce a fresh target table (no conditions applied), wrapped in
45    /// `Box<dyn Any>` so callers can downcast back to the concrete
46    /// `Table<T, TargetE>`. Used by [`crate::table::Table::get_ref_as`] and
47    /// [`crate::table::Table::get_subquery_as`] to build the target before
48    /// applying the join condition. Errors when the target can't be built
49    /// (e.g. a spec reference whose target has no spec).
50    fn build_target(&self, data_source: &dyn Any) -> Result<Box<dyn Any>>;
51
52    /// Produce a fresh target table with its entity type erased to
53    /// `EmptyEntity`, wrapped in `Box<dyn Any>` for downcast to
54    /// `Table<T, EmptyEntity>`. Unlike [`build_target`](Self::build_target),
55    /// the caller need not know the concrete target entity type — used by
56    /// implicit-reference traversal, which walks relations by name.
57    fn build_target_erased(&self, data_source: &dyn Any) -> Result<Box<dyn Any>>;
58
59    /// Resolve traversal using a known source row. Returns the target table
60    /// (entity type erased to `EmptyEntity`) wrapped in `Box<dyn Any>`, with
61    /// one eq-condition applied that selects the related rows.
62    ///
63    /// `data_source` is `&T` for the source's `TableSource`; `source_id_field`
64    /// is the name of the source table's id column (needed by `HasMany` to
65    /// pull the join value out of the row); `source_row` is `&Record<T::Value>`.
66    /// `HasOne` ignores `source_id_field` and reads its stored `foreign_key`
67    /// instead.
68    ///
69    /// Callers immediately downcast the result to `Table<T, EmptyEntity>`.
70    fn resolve_from_row(
71        &self,
72        data_source: &dyn Any,
73        source_id_field: &str,
74        source_row: &dyn Any,
75    ) -> Result<Box<dyn Any>>;
76
77    /// The name of the relation's target in Vista metadata, which a catalog
78    /// resolves by name. A relation declared with a target name
79    /// (`try_with_one` / `try_with_many`, so every spec reference) returns
80    /// it without building the target, so a metadata snapshot runs no
81    /// target build and no injector; traversing it reports any build error.
82    /// Otherwise, the table name of the target built over `data_source`
83    /// (`&T`).
84    fn target_table_name(&self, data_source: &dyn Any) -> String;
85}