vespertide_core/schema/reference.rs
1use serde::{Deserialize, Serialize};
2
3/// The referential action taken on child rows when the referenced parent row changes.
4///
5/// Used in `ForeignKeyDef::on_delete` and `ForeignKeyDef::on_update` to control cascading
6/// behaviour. In JSON model files these are written in `snake_case`
7/// (e.g. `"on_delete": "cascade"`).
8///
9/// This enum is `#[non_exhaustive]`: new variants may be added in future releases.
10/// Downstream `match` expressions should include a wildcard arm.
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
12#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
13#[serde(rename_all = "snake_case")]
14#[non_exhaustive]
15pub enum ReferenceAction {
16 /// Automatically delete or update child rows when the parent row is deleted or updated (`CASCADE`).
17 Cascade,
18 /// Prevent the parent row from being deleted or updated if child rows exist (`RESTRICT`).
19 Restrict,
20 /// Set the foreign key column(s) in child rows to `NULL` when the parent changes (`SET NULL`).
21 /// The column must be nullable.
22 SetNull,
23 /// Set the foreign key column(s) in child rows to their column default when the parent changes (`SET DEFAULT`).
24 SetDefault,
25 /// Do nothing to child rows; the database defers enforcement or raises an error (`NO ACTION`).
26 NoAction,
27}
28
29impl ReferenceAction {
30 /// SQL keyword representation as written in `ALTER TABLE ... ADD
31 /// CONSTRAINT ... FOREIGN KEY ... ON DELETE <keyword>` etc. Used by
32 /// `vespertide-query` when emitting raw SQL (e.g. the F11
33 /// `NOT VALID` + `VALIDATE` PG path, which bypasses the sea-query
34 /// `ForeignKey` builder).
35 #[must_use]
36 pub fn to_sql_keyword(&self) -> &'static str {
37 match self {
38 Self::Cascade => "CASCADE",
39 Self::Restrict => "RESTRICT",
40 Self::SetNull => "SET NULL",
41 Self::SetDefault => "SET DEFAULT",
42 Self::NoAction => "NO ACTION",
43 }
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 //! Coverage-closure tests for `ReferenceAction::to_sql_keyword`.
50 //! Targets `uncovered-detail.json` lines 40, 41, 42
51 //! (`SetNull` / `SetDefault` / `NoAction` match arms).
52 use super::*;
53 use rstest::rstest;
54
55 #[rstest]
56 #[case::cascade(ReferenceAction::Cascade, "CASCADE")]
57 #[case::restrict(ReferenceAction::Restrict, "RESTRICT")]
58 #[case::set_null(ReferenceAction::SetNull, "SET NULL")]
59 #[case::set_default(ReferenceAction::SetDefault, "SET DEFAULT")]
60 #[case::no_action(ReferenceAction::NoAction, "NO ACTION")]
61 fn to_sql_keyword_emits_expected_token(
62 #[case] action: ReferenceAction,
63 #[case] expected: &'static str,
64 ) {
65 // Each rstest case visits one match arm of to_sql_keyword. The
66 // SetNull/SetDefault/NoAction cases cover the previously-uncovered
67 // lines 40, 41, 42.
68 assert_eq!(action.to_sql_keyword(), expected);
69 }
70}