use topcoat::context::Cx;
use super::{Action, HeaderAction, Resource};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Change<M> {
Create(M),
Update { before: M, after: M },
Delete(Vec<M>),
Restore(Vec<M>),
ForceDelete(Vec<M>),
Action { name: &'static str, records: Vec<M> },
HeaderAction { name: &'static str },
Attach {
owner: M,
relation: String,
key: String,
},
Detach {
owner: M,
relation: String,
keys: Vec<String>,
},
}
impl<M> Change<M> {
pub fn records(&self) -> &[M] {
match self {
Self::Create(record)
| Self::Update { after: record, .. }
| Self::Attach { owner: record, .. }
| Self::Detach { owner: record, .. } => std::slice::from_ref(record),
Self::Delete(records)
| Self::Restore(records)
| Self::ForceDelete(records)
| Self::Action { records, .. } => records,
Self::HeaderAction { .. } => &[],
}
}
pub fn is_action<A, R>(&self) -> bool
where
A: Action<R>,
R: Resource<Model = M>,
{
matches!(self, Self::Action { name, .. } if *name == A::NAME)
}
pub fn is_header_action<A: HeaderAction>(&self) -> bool {
matches!(self, Self::HeaderAction { name } if *name == A::NAME)
}
}
pub(crate) fn acted<R: Resource, A: Action<R>>(records: Vec<R::Model>) -> Change<R::Model> {
Change::Action {
name: A::NAME,
records,
}
}
pub(crate) async fn run_after_commit<R: Resource>(cx: &Cx, change: Change<R::Model>) {
if let Err(error) = R::after_commit(cx, change).await {
tracing::error!(
error = %error,
resource = std::any::type_name::<R>(),
"after_commit failed; the write stays committed"
);
}
}