toasty_core/schema/app/relation/has.rs
1use crate::{
2 schema::app::{BelongsTo, FieldId, Model, ModelId, Name, Schema},
3 stmt,
4};
5
6/// The inverse side of a relationship.
7///
8/// A `Has` field on model A reaches an associated model B through a paired
9/// [`BelongsTo`] field on B that holds the foreign key. Its cardinality records
10/// whether the field represents "A has many Bs" or "A has one B".
11#[derive(Debug, Clone)]
12pub struct Has {
13 /// The [`ModelId`] of the associated (target) model.
14 pub target: ModelId,
15
16 /// The expression type this field evaluates to from the application's
17 /// perspective.
18 pub expr_ty: stmt::Type,
19
20 /// Whether this relation is one-to-many or one-to-one.
21 pub cardinality: Cardinality,
22
23 /// The paired `BelongsTo` field on the target model.
24 pub pair_id: FieldId,
25}
26
27/// Cardinality for a relation field that reaches another model.
28#[derive(Debug, Clone)]
29pub enum Cardinality {
30 /// The relation yields zero or more associated items.
31 Many {
32 /// The singular name for one associated item (used in generated method
33 /// names).
34 singular: Name,
35 },
36
37 /// The relation yields at most one associated item.
38 One,
39}
40
41impl Has {
42 /// Returns `true` when this is a one-to-many relation.
43 pub fn is_many(&self) -> bool {
44 self.cardinality.is_many()
45 }
46
47 /// Returns `true` when this is a one-to-one relation.
48 pub fn is_one(&self) -> bool {
49 self.cardinality.is_one()
50 }
51
52 /// Returns the singular item name for a one-to-many relation.
53 pub fn singular(&self) -> Option<&Name> {
54 self.cardinality.singular()
55 }
56
57 /// Resolves the target [`Model`] from the given schema.
58 pub fn target<'a>(&self, schema: &'a Schema) -> &'a Model {
59 schema.model(self.target)
60 }
61
62 /// Resolves the paired [`BelongsTo`] relation on the target model.
63 ///
64 /// # Panics
65 ///
66 /// Panics if the paired field is not a `BelongsTo` variant.
67 pub fn pair<'a>(&self, schema: &'a Schema) -> &'a BelongsTo {
68 schema.field(self.pair_id).ty.as_belongs_to_unwrap()
69 }
70}
71
72impl Cardinality {
73 /// Returns `true` when this relation yields zero or more items.
74 pub fn is_many(&self) -> bool {
75 matches!(self, Cardinality::Many { .. })
76 }
77
78 /// Returns `true` when this relation yields at most one item.
79 pub fn is_one(&self) -> bool {
80 matches!(self, Cardinality::One)
81 }
82
83 /// Returns the singular item name for a one-to-many relation.
84 pub fn singular(&self) -> Option<&Name> {
85 match self {
86 Cardinality::Many { singular } => Some(singular),
87 Cardinality::One => None,
88 }
89 }
90}