Skip to main content

toasty_core/schema/app/relation/
has_many.rs

1use crate::{
2    schema::app::{BelongsTo, HasKind, Model, ModelId, Name, Schema},
3    stmt,
4};
5
6/// The inverse side of a one-to-many relationship.
7///
8/// A `HasMany` field on model A means "A has many Bs". A direct `HasMany`
9/// pairs with a [`BelongsTo`] field on model B that holds the foreign key; a
10/// multi-step (`via`) `HasMany` reaches B by following a path of existing
11/// relations. Which one it is is recorded in [`kind`](HasMany::kind).
12///
13/// # Examples
14///
15/// ```ignore
16/// // Given a `User` model that has many `Post`s:
17/// let has_many: &HasMany = user_field.ty.as_has_many_unwrap();
18/// let post_model = has_many.target(&schema);
19/// let inverse = has_many.pair(&schema); // the BelongsTo on Post
20/// ```
21#[derive(Debug, Clone)]
22pub struct HasMany {
23    /// The [`ModelId`] of the associated (target) model.
24    pub target: ModelId,
25
26    /// The expression type this field evaluates to from the application's
27    /// perspective.
28    pub expr_ty: stmt::Type,
29
30    /// The singular name for one associated item (used in generated method
31    /// names).
32    pub singular: Name,
33
34    /// How this relation reaches its target — a paired `BelongsTo`
35    /// ([`HasKind::Direct`]) or a [`Via`](super::Via) path
36    /// ([`HasKind::Via`]).
37    pub kind: HasKind,
38}
39
40impl HasMany {
41    /// Resolves the target [`Model`] from the given schema.
42    pub fn target<'a>(&self, schema: &'a Schema) -> &'a Model {
43        schema.model(self.target)
44    }
45
46    /// Resolves the paired [`BelongsTo`] relation on the target model.
47    ///
48    /// # Panics
49    ///
50    /// Panics if this is a multi-step (`via`) relation — it has no pair — or
51    /// if the paired field is not a `BelongsTo` variant.
52    pub fn pair<'a>(&self, schema: &'a Schema) -> &'a BelongsTo {
53        let pair = self
54            .kind
55            .pair_id()
56            .expect("`via` relation has no paired `BelongsTo`");
57        schema.field(pair).ty.as_belongs_to_unwrap()
58    }
59}