toasty_core/schema/app/relation/via.rs
1use crate::{schema::app::FieldId, stmt};
2
3/// How a `HasMany` or `HasOne` relation reaches its target.
4///
5/// Both relation kinds share this: the question — "is the target reached
6/// through a paired `BelongsTo`, or by following a path?" — is the same for
7/// has-many and has-one.
8#[derive(Debug, Clone)]
9pub enum HasKind {
10 /// The target is reached through a `BelongsTo` field on the target model.
11 /// Carries that paired `BelongsTo` field's id.
12 ///
13 /// If a `#[has_many(pair = <field>)]` / `#[has_one(pair = <field>)]` was
14 /// supplied, the macro resolves the id at schema-construction time.
15 /// Otherwise the linker fills it in by searching the target model for a
16 /// unique `BelongsTo` back to the source.
17 Direct(FieldId),
18
19 /// The target is reached by following a [`Via`] path of existing
20 /// relations rather than pairing with a single `BelongsTo`.
21 Via(Via),
22}
23
24impl HasKind {
25 /// The paired `BelongsTo` field id, or `None` for a `via` relation.
26 pub fn pair_id(&self) -> Option<FieldId> {
27 match self {
28 HasKind::Direct(pair) => Some(*pair),
29 HasKind::Via(_) => None,
30 }
31 }
32
33 /// The [`Via`] path, or `None` for a direct relation.
34 pub fn via(&self) -> Option<&Via> {
35 match self {
36 HasKind::Via(via) => Some(via),
37 HasKind::Direct(_) => None,
38 }
39 }
40}
41
42/// A multi-step relation path.
43///
44/// A `HasMany` or `HasOne` declared with `#[has_many(via = a.b)]` reaches its
45/// target by following a path of existing relations rather than pairing with a
46/// single `BelongsTo`. The path is resolved at macro-expansion time — the
47/// derive emits a chained call on the model's `Fields` struct
48/// (e.g. `User::fields().comments().article()`) and converts it into a
49/// [`stmt::Path`], so a misspelled or otherwise unresolvable segment is a
50/// Rust compile error rather than a runtime schema validation failure.
51#[derive(Debug, Clone)]
52pub struct Via {
53 /// The resolved field path, rooted at the model that declares the via
54 /// relation.
55 pub path: stmt::Path,
56}
57
58impl Via {
59 /// Create a `Via` from its fully resolved field path.
60 pub fn new(path: stmt::Path) -> Self {
61 Self { path }
62 }
63}