Skip to main content

vantage_table/
active_entity_ext.rs

1//! Relationship traversal from a loaded record handle.
2//!
3//! [`ActiveEntity`] and [`ActiveRecord`] live in `vantage-dataset` and know
4//! nothing about `Table`, so this `Table`-aware extension is provided here as a
5//! trait. It is the record-level equivalent of `table.get_ref_from_row(...)`:
6//! given a launch you loaded with `get_entity` (typed) or `get_value_record`
7//! (untyped), `launch.get_ref::<LaunchCrew>("launch_crew")` returns the child
8//! table scoped to that launch (and carrying the foreign-key invariant, so
9//! inserts conform — see `Table::add_invariant`).
10//!
11//! `ActiveEntity` wraps a typed struct with no id column, so it serializes the
12//! entity and injects its id before traversing. `ActiveRecord` already holds the
13//! raw row (including id/foreign-key columns), so its impl forwards directly.
14
15use vantage_core::{Result, error};
16use vantage_dataset::prelude::{ActiveEntity, ActiveRecord};
17use vantage_types::{Entity, InvariantValue, TryIntoRecord};
18
19use crate::{
20    table::Table,
21    traits::{column_like::ColumnLike, table_source::TableSource},
22};
23
24/// Traverse a relation from a loaded record.
25pub trait GetRefExt<T: TableSource, E> {
26    /// Return the related set for `relation`, reading the join value out of the
27    /// in-memory entity. Errors if the relation is unknown to the table.
28    fn get_ref<E2: Entity<T::Value> + 'static>(&self, relation: &str) -> Result<Table<T, E2>>;
29}
30
31impl<'a, T, E> GetRefExt<T, E> for ActiveEntity<'a, Table<T, E>, E>
32where
33    T: TableSource,
34    T::Id: Into<T::Value>,
35    T::Value: InvariantValue,
36    E: Entity<T::Value> + 'static,
37    <E as TryIntoRecord<T::Value>>::Error: std::fmt::Debug,
38{
39    fn get_ref<E2: Entity<T::Value> + 'static>(&self, relation: &str) -> Result<Table<T, E2>> {
40        let mut record = self
41            .data()
42            .clone()
43            .try_into_record()
44            .map_err(|e| error!("Failed to serialize entity to record", error = e))?;
45
46        // The entity struct carries no id column, but has-many traversal reads the
47        // parent id out of the row — inject it from the ActiveEntity's known id.
48        let id_field = self
49            .dataset()
50            .id_field()
51            .map(|c| c.name().to_string())
52            .unwrap_or_else(|| "id".to_string());
53        record.insert(id_field, self.id().clone().into());
54
55        self.dataset().get_ref_from_row::<E2>(relation, &record)
56    }
57}
58
59impl<'a, T, E> GetRefExt<T, E> for ActiveRecord<'a, Table<T, E>>
60where
61    T: TableSource,
62    T::Value: InvariantValue,
63    E: Entity<T::Value> + 'static,
64{
65    fn get_ref<E2: Entity<T::Value> + 'static>(&self, relation: &str) -> Result<Table<T, E2>> {
66        // The raw row is already in hand (id and foreign keys included), so unlike
67        // the `ActiveEntity` impl there is nothing to serialize or inject.
68        let row: &vantage_types::Record<T::Value> = self;
69        self.dataset().get_ref_from_row::<E2>(relation, row)
70    }
71}