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_with_structural_readers,
12 },
13 relation::StrongRelationDeleteValidateFn,
14 },
15 error::InternalError,
16 model::entity::EntityModel,
17 traits::{CanisterKind, EntityKind, EntityValue, Path},
18 types::EntityTag,
19};
20
21type PrepareRowCommitWithReadersFn<C> = fn(
24 &Db<C>,
25 &CommitRowOp,
26 &dyn StructuralPrimaryRowReader,
27 &dyn StructuralIndexEntryReader,
28) -> Result<PreparedRowCommitOp, InternalError>;
29
30pub struct EntityRuntimeHooks<C: CanisterKind> {
41 pub(crate) entity_tag: EntityTag,
42 pub(crate) model: &'static EntityModel,
43 pub(crate) entity_path: &'static str,
44 pub(crate) store_path: &'static str,
45 pub(in crate::db) prepare_row_commit_with_readers: PrepareRowCommitWithReadersFn<C>,
46 pub(crate) validate_delete_strong_relations: StrongRelationDeleteValidateFn<C>,
47}
48
49impl<C: CanisterKind> EntityRuntimeHooks<C> {
50 #[must_use]
52 pub(in crate::db) const fn new(
53 entity_tag: EntityTag,
54 model: &'static EntityModel,
55 entity_path: &'static str,
56 store_path: &'static str,
57 prepare_row_commit_with_readers: PrepareRowCommitWithReadersFn<C>,
58 validate_delete_strong_relations: StrongRelationDeleteValidateFn<C>,
59 ) -> Self {
60 Self {
61 entity_tag,
62 model,
63 entity_path,
64 store_path,
65 prepare_row_commit_with_readers,
66 validate_delete_strong_relations,
67 }
68 }
69
70 #[must_use]
72 pub const fn for_entity<E>() -> Self
73 where
74 E: EntityKind<Canister = C> + EntityValue,
75 {
76 Self::new(
77 E::ENTITY_TAG,
78 E::MODEL,
79 E::PATH,
80 E::Store::PATH,
81 prepare_row_commit_for_entity_with_structural_readers::<E>,
82 crate::db::relation::validate_delete_strong_relations_for_source::<E>,
83 )
84 }
85}
86
87#[must_use]
89pub(in crate::db) const fn has_runtime_hooks<C: CanisterKind>(
90 entity_runtime_hooks: &[EntityRuntimeHooks<C>],
91) -> bool {
92 !entity_runtime_hooks.is_empty()
93}
94
95#[must_use]
100#[cfg(debug_assertions)]
101pub(in crate::db) const fn debug_assert_unique_runtime_hook_tags<C: CanisterKind>(
102 entity_runtime_hooks: &[EntityRuntimeHooks<C>],
103) -> bool {
104 let mut i = 0usize;
105 while i < entity_runtime_hooks.len() {
106 let mut j = i + 1;
107 while j < entity_runtime_hooks.len() {
108 if entity_runtime_hooks[i].entity_tag.value()
109 == entity_runtime_hooks[j].entity_tag.value()
110 {
111 panic!("duplicate EntityTag detected in runtime hooks");
112 }
113 j += 1;
114 }
115 i += 1;
116 }
117
118 true
119}
120
121pub(in crate::db) fn resolve_runtime_hook_by_tag<C: CanisterKind>(
124 entity_runtime_hooks: &[EntityRuntimeHooks<C>],
125 entity_tag: EntityTag,
126) -> Result<&EntityRuntimeHooks<C>, InternalError> {
127 let mut matched = None;
128 for hooks in entity_runtime_hooks {
129 if hooks.entity_tag != entity_tag {
130 continue;
131 }
132
133 if matched.is_some() {
134 return Err(InternalError::duplicate_runtime_hooks_for_entity_tag(
135 entity_tag,
136 ));
137 }
138
139 matched = Some(hooks);
140 }
141
142 matched.ok_or_else(|| InternalError::unsupported_entity_tag_in_data_store(entity_tag))
143}
144
145pub(in crate::db) fn resolve_runtime_hook_by_path<'a, C: CanisterKind>(
148 entity_runtime_hooks: &'a [EntityRuntimeHooks<C>],
149 entity_path: &str,
150) -> Result<&'a EntityRuntimeHooks<C>, InternalError> {
151 let mut matched = None;
152 for hooks in entity_runtime_hooks {
153 if hooks.entity_path != entity_path {
154 continue;
155 }
156
157 if matched.is_some() {
158 return Err(InternalError::duplicate_runtime_hooks_for_entity_path(
159 entity_path,
160 ));
161 }
162
163 matched = Some(hooks);
164 }
165
166 matched.ok_or_else(|| InternalError::unsupported_entity_path(entity_path))
167}