icydb_core/db/commit/
hooks.rs1use crate::{
7 db::{
8 Db,
9 commit::{
10 CommitRowOp, CommitSchemaFingerprint, PreparedRowCommitOp,
11 prepare_row_commit_for_entity,
12 },
13 relation::StrongRelationDeleteValidateFn,
14 schema::commit_schema_fingerprint_for_entity,
15 },
16 error::InternalError,
17 traits::{CanisterKind, EntityIdentity, EntityKind, EntityValue},
18 types::EntityTag,
19};
20
21pub struct EntityRuntimeHooks<C: CanisterKind> {
29 pub(crate) entity_tag: EntityTag,
30 pub(crate) entity_name: &'static str,
31 pub(crate) entity_path: &'static str,
32 pub(in crate::db) commit_schema_fingerprint: fn() -> CommitSchemaFingerprint,
33 pub(in crate::db) prepare_row_commit:
34 fn(&Db<C>, &CommitRowOp) -> Result<PreparedRowCommitOp, InternalError>,
35 pub(crate) validate_delete_strong_relations: StrongRelationDeleteValidateFn<C>,
36}
37
38impl<C: CanisterKind> EntityRuntimeHooks<C> {
39 #[must_use]
41 pub(in crate::db) const fn new(
42 entity_tag: EntityTag,
43 entity_name: &'static str,
44 entity_path: &'static str,
45 commit_schema_fingerprint: fn() -> CommitSchemaFingerprint,
46 prepare_row_commit: fn(&Db<C>, &CommitRowOp) -> Result<PreparedRowCommitOp, InternalError>,
47 validate_delete_strong_relations: StrongRelationDeleteValidateFn<C>,
48 ) -> Self {
49 Self {
50 entity_tag,
51 entity_name,
52 entity_path,
53 commit_schema_fingerprint,
54 prepare_row_commit,
55 validate_delete_strong_relations,
56 }
57 }
58
59 #[must_use]
61 pub const fn for_entity<E>() -> Self
62 where
63 E: EntityKind<Canister = C> + EntityValue,
64 {
65 Self::new(
66 E::ENTITY_TAG,
67 <E as EntityIdentity>::ENTITY_NAME,
68 E::PATH,
69 commit_schema_fingerprint_for_runtime_entity::<E>,
70 prepare_row_commit_for_entity::<E>,
71 crate::db::relation::validate_delete_strong_relations_for_source::<E>,
72 )
73 }
74}
75
76fn commit_schema_fingerprint_for_runtime_entity<E>() -> CommitSchemaFingerprint
77where
78 E: EntityKind,
79{
80 commit_schema_fingerprint_for_entity::<E>()
81}
82
83#[must_use]
85pub(in crate::db) const fn has_runtime_hooks<C: CanisterKind>(
86 entity_runtime_hooks: &[EntityRuntimeHooks<C>],
87) -> bool {
88 !entity_runtime_hooks.is_empty()
89}
90
91#[must_use]
96#[cfg(debug_assertions)]
97pub(in crate::db) const fn debug_assert_unique_runtime_hook_tags<C: CanisterKind>(
98 entity_runtime_hooks: &[EntityRuntimeHooks<C>],
99) -> bool {
100 let mut i = 0usize;
101 while i < entity_runtime_hooks.len() {
102 let mut j = i + 1;
103 while j < entity_runtime_hooks.len() {
104 if entity_runtime_hooks[i].entity_tag.value()
105 == entity_runtime_hooks[j].entity_tag.value()
106 {
107 panic!("duplicate EntityTag detected in runtime hooks");
108 }
109 j += 1;
110 }
111 i += 1;
112 }
113
114 true
115}
116
117pub(in crate::db) fn resolve_runtime_hook_by_tag<C: CanisterKind>(
120 entity_runtime_hooks: &[EntityRuntimeHooks<C>],
121 entity_tag: EntityTag,
122) -> Result<&EntityRuntimeHooks<C>, InternalError> {
123 let mut matched = None;
124 for hooks in entity_runtime_hooks {
125 if hooks.entity_tag != entity_tag {
126 continue;
127 }
128
129 if matched.is_some() {
130 return Err(InternalError::store_invariant(format!(
131 "duplicate runtime hooks for entity tag '{}'",
132 entity_tag.value()
133 )));
134 }
135
136 matched = Some(hooks);
137 }
138
139 matched.ok_or_else(|| {
140 InternalError::store_unsupported(format!(
141 "unsupported entity tag in data store: '{}'",
142 entity_tag.value()
143 ))
144 })
145}
146
147pub(in crate::db) fn resolve_runtime_hook_by_path<'a, C: CanisterKind>(
150 entity_runtime_hooks: &'a [EntityRuntimeHooks<C>],
151 entity_path: &str,
152) -> Result<&'a EntityRuntimeHooks<C>, InternalError> {
153 let mut matched = None;
154 for hooks in entity_runtime_hooks {
155 if hooks.entity_path != entity_path {
156 continue;
157 }
158
159 if matched.is_some() {
160 return Err(InternalError::store_invariant(format!(
161 "duplicate runtime hooks for entity path '{entity_path}'"
162 )));
163 }
164
165 matched = Some(hooks);
166 }
167
168 matched.ok_or_else(|| InternalError::unsupported_entity_path(entity_path))
169}