1use std::cell::{Cell, RefCell};
11
12use wasm_dbms_api::prelude::{
13 DbmsResult, IdentityPerms, PermGrant, PermRevoke, TableFingerprint, TablePerms, TransactionId,
14};
15use wasm_dbms_memory::prelude::{
16 AccessControl, AccessControlList, MemoryManager, MemoryProvider, SchemaRegistry,
17 TableRegistryPage,
18};
19
20use crate::transaction::journal::Journal;
21use crate::transaction::session::TransactionSession;
22
23pub struct DbmsContext<M, A = AccessControlList>
41where
42 M: MemoryProvider,
43 A: AccessControl,
44{
45 pub(crate) mm: RefCell<MemoryManager<M>>,
47
48 pub(crate) schema_registry: RefCell<SchemaRegistry>,
50
51 pub(crate) acl: RefCell<A>,
53
54 pub(crate) transaction_session: RefCell<TransactionSession>,
56
57 pub(crate) journal: RefCell<Option<Journal>>,
59
60 pub(crate) drift: Cell<Option<(u64, bool)>>,
63
64 pub(crate) migrating: Cell<bool>,
68}
69
70impl<M> DbmsContext<M>
71where
72 M: MemoryProvider,
73{
74 pub fn new(memory: M) -> Self {
77 let mut mm = MemoryManager::init(memory);
78 let schema_registry = SchemaRegistry::load(&mut mm).unwrap_or_default();
79 let acl = AccessControlList::load(&mut mm).unwrap_or_default();
80
81 Self {
82 mm: RefCell::new(mm),
83 schema_registry: RefCell::new(schema_registry),
84 acl: RefCell::new(acl),
85 transaction_session: RefCell::new(TransactionSession::default()),
86 journal: RefCell::new(None),
87 drift: Cell::new(None),
88 migrating: Cell::new(false),
89 }
90 }
91}
92
93impl<M, A> DbmsContext<M, A>
94where
95 M: MemoryProvider,
96 A: AccessControl,
97{
98 pub fn with_acl(memory: M) -> Self {
100 let mut mm = MemoryManager::init(memory);
101 let schema_registry = SchemaRegistry::load(&mut mm).unwrap_or_default();
102 let acl = A::load(&mut mm).unwrap_or_default();
103
104 Self {
105 mm: RefCell::new(mm),
106 schema_registry: RefCell::new(schema_registry),
107 acl: RefCell::new(acl),
108 transaction_session: RefCell::new(TransactionSession::default()),
109 journal: RefCell::new(None),
110 drift: Cell::new(None),
111 migrating: Cell::new(false),
112 }
113 }
114
115 pub fn register_table<T: wasm_dbms_api::prelude::TableSchema>(
117 &self,
118 ) -> DbmsResult<TableRegistryPage> {
119 let mut sr = self.schema_registry.borrow_mut();
120 let mut mm = self.mm.borrow_mut();
121 sr.register_table::<T>(&mut *mm).map_err(Into::into)
122 }
123
124 pub fn has_table(&self, name: &str) -> bool {
126 self.schema_registry
127 .borrow()
128 .table_registry_page_by_name(name)
129 .is_some()
130 }
131
132 pub fn granted(&self, id: &A::Id, table: TableFingerprint, required: TablePerms) -> bool {
134 self.acl.borrow().granted(id, table, required)
135 }
136
137 pub fn granted_admin(&self, id: &A::Id) -> bool {
139 self.acl.borrow().granted_admin(id)
140 }
141
142 pub fn granted_manage_acl(&self, id: &A::Id) -> bool {
144 self.acl.borrow().granted_manage_acl(id)
145 }
146
147 pub fn granted_migrate(&self, id: &A::Id) -> bool {
149 self.acl.borrow().granted_migrate(id)
150 }
151
152 pub fn acl_grant(&self, id: A::Id, grant: PermGrant) -> DbmsResult<()> {
156 let mut acl = self.acl.borrow_mut();
157 let mut mm = self.mm.borrow_mut();
158 acl.grant(id, grant, &mut mm).map_err(Into::into)
159 }
160
161 pub fn acl_revoke(&self, id: &A::Id, revoke: PermRevoke) -> DbmsResult<()> {
163 let mut acl = self.acl.borrow_mut();
164 let mut mm = self.mm.borrow_mut();
165 acl.revoke(id, revoke, &mut mm).map_err(Into::into)
166 }
167
168 pub fn acl_remove_identity(&self, id: &A::Id) -> DbmsResult<()> {
171 let mut acl = self.acl.borrow_mut();
172 let mut mm = self.mm.borrow_mut();
173 acl.remove_identity(id, &mut mm).map_err(Into::into)
174 }
175
176 pub fn acl_perms(&self, id: &A::Id) -> IdentityPerms {
178 self.acl.borrow().perms(id)
179 }
180
181 pub fn acl_identities(&self) -> Vec<(A::Id, IdentityPerms)> {
183 self.acl.borrow().identities()
184 }
185
186 pub fn begin_transaction(&self, owner: Vec<u8>) -> TransactionId {
188 let mut ts = self.transaction_session.borrow_mut();
189 ts.begin_transaction(owner)
190 }
191
192 pub fn has_transaction(&self, tx_id: &TransactionId, caller: &[u8]) -> bool {
194 let ts = self.transaction_session.borrow();
195 ts.has_transaction(tx_id, caller)
196 }
197
198 pub(crate) fn cached_drift_for(&self, compiled_hash: u64) -> Option<bool> {
200 self.drift
201 .get()
202 .and_then(|(hash, drifted)| (hash == compiled_hash).then_some(drifted))
203 }
204
205 pub(crate) fn set_drift(&self, compiled_hash: u64, value: bool) {
207 self.drift.set(Some((compiled_hash, value)));
208 }
209
210 pub(crate) fn clear_drift(&self) {
212 self.drift.set(None);
213 }
214
215 pub(crate) fn is_migrating(&self) -> bool {
217 self.migrating.get()
218 }
219
220 pub(crate) fn set_migrating(&self, value: bool) {
222 self.migrating.set(value);
223 }
224}
225
226impl<M, A> std::fmt::Debug for DbmsContext<M, A>
227where
228 M: MemoryProvider,
229 A: AccessControl + std::fmt::Debug,
230{
231 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232 f.debug_struct("DbmsContext")
233 .field("schema_registry", &self.schema_registry)
234 .field("acl", &self.acl)
235 .field("transaction_session", &self.transaction_session)
236 .finish_non_exhaustive()
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use wasm_dbms_memory::prelude::HeapMemoryProvider;
243
244 use super::*;
245
246 #[test]
247 fn test_should_create_context() {
248 let ctx = DbmsContext::new(HeapMemoryProvider::default());
249 assert!(ctx.acl_identities().is_empty());
250 }
251
252 #[test]
253 fn test_should_grant_admin_to_identity() {
254 let ctx = DbmsContext::new(HeapMemoryProvider::default());
255 ctx.acl_grant(vec![1, 2, 3], PermGrant::Admin).unwrap();
256 assert!(ctx.granted_admin(&vec![1, 2, 3]));
257 assert!(!ctx.granted_admin(&vec![4, 5, 6]));
258 }
259
260 #[test]
261 fn test_should_remove_identity() {
262 let ctx = DbmsContext::new(HeapMemoryProvider::default());
263 ctx.acl_grant(vec![1, 2, 3], PermGrant::ManageAcl).unwrap();
264 ctx.acl_grant(vec![4, 5, 6], PermGrant::Admin).unwrap();
265 ctx.acl_remove_identity(&vec![4, 5, 6]).unwrap();
266 assert!(!ctx.granted_admin(&vec![4, 5, 6]));
267 assert!(ctx.granted_manage_acl(&vec![1, 2, 3]));
268 }
269
270 #[test]
271 fn test_should_begin_transaction() {
272 let ctx = DbmsContext::new(HeapMemoryProvider::default());
273 let owner = vec![1, 2, 3];
274 let tx_id = ctx.begin_transaction(owner.clone());
275 assert!(ctx.has_transaction(&tx_id, &owner));
276 assert!(!ctx.has_transaction(&tx_id, &[4, 5, 6]));
277 }
278
279 #[test]
280 fn test_should_debug_context() {
281 let ctx = DbmsContext::new(HeapMemoryProvider::default());
282 let debug = format!("{ctx:?}");
283 assert!(debug.contains("DbmsContext"));
284 }
285}