Skip to main content

tablo_core/declaration/
many_to_many.rs

1//! The declaration mistakes of a many-to-many field or relation, and their wording.
2
3use std::fmt;
4
5/// Why a many-to-many field or relation cannot link records:
6/// [`DeclarationErrorKind::ManyToMany`](super::DeclarationErrorKind::ManyToMany).
7#[derive(Debug, Clone, PartialEq, Eq)]
8#[non_exhaustive]
9pub enum ManyToManyFault {
10    /// The field is no `#[has_many(via = joins.target)]` stepping through a `has_many` to a join
11    /// model, then the join model's `belongs_to` to the target.
12    ///
13    /// Declare it through a `has_many` to the join model and the join model's `belongs_to`.
14    NotJoinModel,
15    /// A foreign key of the join model spans several columns, or references no single-column
16    /// primary key.
17    ///
18    /// Key each side by one column.
19    CompositeKey,
20    /// The join model has a column a new link cannot fill: not nullable, not `#[auto]`, and not
21    /// one of its two foreign keys.
22    ///
23    /// Make it nullable or `#[auto]`, or link the records in your own record fn.
24    UnwritableColumn {
25        /// The join model.
26        model: String,
27        /// The column.
28        column: String,
29    },
30    /// The form's control for the field is not a multiple choice over a relationship.
31    ///
32    /// Declare `.choice().relationship::<R>().multiple()`.
33    NotMultipleChoice,
34    /// The relationship's records are not the target's.
35    ///
36    /// Point the relationship at the target's resource.
37    OtherSource,
38    /// A multiple choice in a resource's form over no many-to-many field: a relationship's keys,
39    /// which no column stores, or static options over a column holding one value.
40    ///
41    /// Bind a relationship to a `#[has_many(via = ..)]` field, and static options to a `Vec`.
42    NoJoin,
43}
44
45/// [`DeclarationErrorKind::ManyToMany`](super::DeclarationErrorKind::ManyToMany)'s sentence.
46pub(super) fn fmt_many_to_many(
47    f: &mut fmt::Formatter<'_>,
48    field: &str,
49    fault: &ManyToManyFault,
50) -> fmt::Result {
51    match fault {
52        ManyToManyFault::NotJoinModel => write!(
53            f,
54            "`{field}` is no many-to-many field: declare it \
55             `#[has_many(via = joins.target)]`, through a `has_many` to a join model and \
56             the join model's `belongs_to` to the target"
57        ),
58        ManyToManyFault::CompositeKey => write!(
59            f,
60            "`{field}`'s join model holds a key of several columns, or one referencing no \
61             single-column primary key: a link writes one column per side"
62        ),
63        ManyToManyFault::UnwritableColumn { model, column } => write!(
64            f,
65            "`{field}`'s join model `{model}` has a column `{column}` a new link cannot \
66             fill: make it nullable or `#[auto]`, or link the records in your own record fn"
67        ),
68        ManyToManyFault::NotMultipleChoice => write!(
69            f,
70            "`{field}` is a many-to-many field, so its control is a multiple choice over \
71             a relationship: `.choice().relationship::<R>().multiple()`"
72        ),
73        ManyToManyFault::OtherSource => write!(
74            f,
75            "`{field}` offers another model's records than the ones its join model links"
76        ),
77        ManyToManyFault::NoJoin => write!(
78            f,
79            "multiple choice `{field}` names neither a many-to-many field of the model nor a \
80             list column, so nothing would store its choices: bind a relationship to a \
81             `#[has_many(via = ..)]` field, and static options to a `Vec`"
82        ),
83    }
84}