toasty_core/schema/app/relation/has_one.rs
1use crate::{
2 schema::app::{BelongsTo, FieldId, FieldTy, Model, ModelId, Schema},
3 stmt,
4};
5
6/// The inverse side of a one-to-one relationship.
7///
8/// A `HasOne` field on model A means "A has exactly one B". The actual foreign
9/// key lives on model B as a [`BelongsTo`] field. The two sides are linked via
10/// the [`pair`](HasOne::pair) field.
11///
12/// # Examples
13///
14/// ```ignore
15/// // Given a `User` model that has one `Profile`:
16/// let has_one: &HasOne = user_field.ty.as_has_one_unwrap();
17/// let profile_model = has_one.target(&schema);
18/// let inverse = has_one.pair(&schema); // the BelongsTo on Profile
19/// ```
20#[derive(Debug, Clone)]
21pub struct HasOne {
22 /// The [`ModelId`] of the associated (target) model.
23 pub target: ModelId,
24
25 /// The expression type this field evaluates to from the application's
26 /// perspective.
27 pub expr_ty: stmt::Type,
28
29 /// The [`BelongsTo`] field on the target model that pairs with this
30 /// relation. If a `#[has_one(pair = <field>)]` was supplied, the macro
31 /// resolves this at schema-construction time via `field_name_to_id` on
32 /// the target. Otherwise the linker fills it in by searching the target
33 /// model for a unique `BelongsTo` back to the source.
34 pub pair: FieldId,
35}
36
37impl HasOne {
38 /// Resolves the target [`Model`] from the given schema.
39 pub fn target<'a>(&self, schema: &'a Schema) -> &'a Model {
40 schema.model(self.target)
41 }
42
43 /// Resolves the paired [`BelongsTo`] relation on the target model.
44 ///
45 /// # Panics
46 ///
47 /// Panics if the paired field is not a `BelongsTo` variant.
48 pub fn pair<'a>(&self, schema: &'a Schema) -> &'a BelongsTo {
49 schema.field(self.pair).ty.as_belongs_to_unwrap()
50 }
51}
52
53impl From<HasOne> for FieldTy {
54 fn from(value: HasOne) -> Self {
55 Self::HasOne(value)
56 }
57}