Skip to main content

tablo_core/resource/
commit.rs

1//! What a mutation committed, and the one place the framework says so.
2
3use topcoat::context::Cx;
4
5use super::{Action, Resource};
6
7/// The kind of mutation a record fn performed.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[non_exhaustive]
10pub enum Mutation {
11    Create,
12    Update,
13    Delete,
14    /// The custom [`Action`](super::Action) or [`HeaderAction`](crate::HeaderAction) of this
15    /// `NAME`. A header action's [`Committed`] holds no record.
16    Action(&'static str),
17    /// A many-to-many relation's table linked a record to the one [`Committed`] holds.
18    Attach,
19    /// A many-to-many relation's table unlinked records from the one [`Committed`] holds.
20    Detach,
21}
22
23/// What one committed mutation wrote.
24#[derive(Debug, Clone)]
25pub struct Committed<M> {
26    mutation: Mutation,
27    records: Vec<M>,
28}
29
30impl<M> Committed<M> {
31    /// The row a create wrote.
32    pub fn created(record: M) -> Self {
33        Self {
34            mutation: Mutation::Create,
35            records: vec![record],
36        }
37    }
38
39    /// The row an update wrote, as it stands after the write.
40    pub fn updated(record: M) -> Self {
41        Self {
42            mutation: Mutation::Update,
43            records: vec![record],
44        }
45    }
46
47    /// The rows a delete removed, as they were before the delete.
48    pub fn deleted(records: Vec<M>) -> Self {
49        Self {
50            mutation: Mutation::Delete,
51            records,
52        }
53    }
54
55    /// The rows a custom [`Action`](super::Action) `A` ran on.
56    pub fn acted<R: Resource<Model = M>, A: Action<R>>(records: Vec<M>) -> Self {
57        Self {
58            mutation: Mutation::Action(A::NAME),
59            records,
60        }
61    }
62
63    /// A relation's table linked a record to `owner`.
64    pub(crate) fn attached(owner: M) -> Self {
65        Self {
66            mutation: Mutation::Attach,
67            records: vec![owner],
68        }
69    }
70
71    /// A relation's table unlinked records from `owner`.
72    pub(crate) fn detached(owner: M) -> Self {
73        Self {
74            mutation: Mutation::Detach,
75            records: vec![owner],
76        }
77    }
78
79    /// The header action named `name` ran: it acts on no record.
80    pub(crate) fn ran(name: &'static str) -> Self {
81        Self {
82            mutation: Mutation::Action(name),
83            records: Vec::new(),
84        }
85    }
86
87    /// Which record fn or custom action ran.
88    pub fn mutation(&self) -> Mutation {
89        self.mutation
90    }
91
92    /// The rows the mutation wrote, in the order the handler had them.
93    pub fn records(&self) -> &[M] {
94        &self.records
95    }
96}
97
98/// Deliver a committed mutation to the app.
99///
100/// A hook returning `Err` is logged and ignored: the write stays committed.
101pub(crate) async fn run_after_commit<R: Resource>(cx: &Cx, committed: Committed<R::Model>) {
102    if let Err(error) = R::after_commit(cx, committed).await {
103        tracing::error!(
104            error = %error,
105            resource = std::any::type_name::<R>(),
106            "after_commit failed; the write stays committed"
107        );
108    }
109}