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 /// Resolves the target [`Model`] from the given schema.
53 pub fn target<'a>(&self, schema: &'a Schema) -> &'a Model {
54 schema.model(self.target)
55 }
56
57 /// Resolves the paired [`BelongsTo`] relation on the target model.
58 ///
59 /// # Panics
60 ///
61 /// Panics if the paired field is not a `BelongsTo` variant.
62 pub fn pair<'a>(&self, schema: &'a Schema) -> &'a BelongsTo {
63 schema.field(self.pair_id).ty.as_belongs_to_unwrap()
64 }
65}
66
67impl Cardinality {
68 /// Returns `true` when this relation yields zero or more items.
69 pub fn is_many(&self) -> bool {
70 matches!(self, Cardinality::Many { .. })
71 }
72
73 /// Returns `true` when this relation yields at most one item.
74 pub fn is_one(&self) -> bool {
75 matches!(self, Cardinality::One)
76 }
77}