icydb_core/db/commit/
hooks.rs1use crate::{
7 db::index::{StructuralIndexEntryReader, StructuralPrimaryRowReader},
8 db::{
9 Db,
10 commit::{
11 CommitRowOp, PreparedRowCommitOp, prepare_row_commit_for_entity,
12 prepare_row_commit_for_entity_with_structural_readers,
13 },
14 relation::StrongRelationDeleteValidateFn,
15 },
16 error::InternalError,
17 model::entity::EntityModel,
18 traits::{CanisterKind, EntityKind, EntityValue, Path},
19 types::EntityTag,
20};
21
22type PrepareRowCommitWithReadersFn<C> = fn(
25 &Db<C>,
26 &CommitRowOp,
27 &dyn StructuralPrimaryRowReader,
28 &dyn StructuralIndexEntryReader,
29) -> Result<PreparedRowCommitOp, InternalError>;
30
31type PrepareRowCommitFn<C> = fn(&Db<C>, &CommitRowOp) -> Result<PreparedRowCommitOp, InternalError>;
33
34pub struct EntityRuntimeHooks<C: CanisterKind> {
45 pub(crate) entity_tag: EntityTag,
46 pub(crate) model: &'static EntityModel,
47 pub(crate) entity_path: &'static str,
48 pub(crate) store_path: &'static str,
49 pub(in crate::db) prepare_row_commit: PrepareRowCommitFn<C>,
50 pub(in crate::db) prepare_row_commit_with_readers: PrepareRowCommitWithReadersFn<C>,
51 pub(crate) validate_delete_strong_relations: StrongRelationDeleteValidateFn<C>,
52}
53
54impl<C: CanisterKind> EntityRuntimeHooks<C> {
55 #[must_use]
57 pub(in crate::db) const fn new(
58 entity_tag: EntityTag,
59 model: &'static EntityModel,
60 entity_path: &'static str,
61 store_path: &'static str,
62 prepare_row_commit: PrepareRowCommitFn<C>,
63 prepare_row_commit_with_readers: PrepareRowCommitWithReadersFn<C>,
64 validate_delete_strong_relations: StrongRelationDeleteValidateFn<C>,
65 ) -> Self {
66 Self {
67 entity_tag,
68 model,
69 entity_path,
70 store_path,
71 prepare_row_commit,
72 prepare_row_commit_with_readers,
73 validate_delete_strong_relations,
74 }
75 }
76
77 #[must_use]
79 pub const fn for_entity<E>() -> Self
80 where
81 E: EntityKind<Canister = C> + EntityValue,
82 {
83 Self::new(
84 E::ENTITY_TAG,
85 E::MODEL,
86 E::PATH,
87 E::Store::PATH,
88 prepare_row_commit_for_entity::<E>,
89 prepare_row_commit_for_entity_with_structural_readers::<E>,
90 crate::db::relation::validate_delete_strong_relations_for_source::<E>,
91 )
92 }
93}
94
95#[must_use]
97pub(in crate::db) const fn has_runtime_hooks<C: CanisterKind>(
98 entity_runtime_hooks: &[EntityRuntimeHooks<C>],
99) -> bool {
100 !entity_runtime_hooks.is_empty()
101}
102
103#[must_use]
108#[cfg(debug_assertions)]
109pub(in crate::db) const fn debug_assert_unique_runtime_hook_tags<C: CanisterKind>(
110 entity_runtime_hooks: &[EntityRuntimeHooks<C>],
111) -> bool {
112 let mut i = 0usize;
113 while i < entity_runtime_hooks.len() {
114 let mut j = i + 1;
115 while j < entity_runtime_hooks.len() {
116 if entity_runtime_hooks[i].entity_tag.value()
117 == entity_runtime_hooks[j].entity_tag.value()
118 {
119 panic!("duplicate EntityTag detected in runtime hooks");
120 }
121 j += 1;
122 }
123 i += 1;
124 }
125
126 true
127}
128
129pub(in crate::db) fn resolve_runtime_hook_by_tag<C: CanisterKind>(
132 entity_runtime_hooks: &[EntityRuntimeHooks<C>],
133 entity_tag: EntityTag,
134) -> Result<&EntityRuntimeHooks<C>, InternalError> {
135 let mut matched = None;
136 for hooks in entity_runtime_hooks {
137 if hooks.entity_tag != entity_tag {
138 continue;
139 }
140
141 if matched.is_some() {
142 return Err(InternalError::store_invariant(format!(
143 "duplicate runtime hooks for entity tag '{}'",
144 entity_tag.value()
145 )));
146 }
147
148 matched = Some(hooks);
149 }
150
151 matched.ok_or_else(|| {
152 InternalError::store_unsupported(format!(
153 "unsupported entity tag in data store: '{}'",
154 entity_tag.value()
155 ))
156 })
157}
158
159pub(in crate::db) fn resolve_runtime_hook_by_path<'a, C: CanisterKind>(
162 entity_runtime_hooks: &'a [EntityRuntimeHooks<C>],
163 entity_path: &str,
164) -> Result<&'a EntityRuntimeHooks<C>, InternalError> {
165 let mut matched = None;
166 for hooks in entity_runtime_hooks {
167 if hooks.entity_path != entity_path {
168 continue;
169 }
170
171 if matched.is_some() {
172 return Err(InternalError::store_invariant(format!(
173 "duplicate runtime hooks for entity path '{entity_path}'"
174 )));
175 }
176
177 matched = Some(hooks);
178 }
179
180 matched.ok_or_else(|| InternalError::unsupported_entity_path(entity_path))
181}