use std::cell::{Cell, RefCell};
use wasm_dbms_api::prelude::{
DbmsResult, IdentityPerms, PermGrant, PermRevoke, TableFingerprint, TablePerms, TransactionId,
};
use wasm_dbms_memory::prelude::{
AccessControl, AccessControlList, MemoryManager, MemoryProvider, SchemaRegistry,
TableRegistryPage,
};
use crate::transaction::journal::Journal;
use crate::transaction::session::TransactionSession;
pub struct DbmsContext<M, A = AccessControlList>
where
M: MemoryProvider,
A: AccessControl,
{
pub(crate) mm: RefCell<MemoryManager<M>>,
pub(crate) schema_registry: RefCell<SchemaRegistry>,
pub(crate) acl: RefCell<A>,
pub(crate) transaction_session: RefCell<TransactionSession>,
pub(crate) journal: RefCell<Option<Journal>>,
pub(crate) drift: Cell<Option<(u64, bool)>>,
pub(crate) migrating: Cell<bool>,
}
impl<M> DbmsContext<M>
where
M: MemoryProvider,
{
pub fn new(memory: M) -> Self {
let mut mm = MemoryManager::init(memory);
let schema_registry = SchemaRegistry::load(&mut mm).unwrap_or_default();
let acl = AccessControlList::load(&mut mm).unwrap_or_default();
Self {
mm: RefCell::new(mm),
schema_registry: RefCell::new(schema_registry),
acl: RefCell::new(acl),
transaction_session: RefCell::new(TransactionSession::default()),
journal: RefCell::new(None),
drift: Cell::new(None),
migrating: Cell::new(false),
}
}
}
impl<M, A> DbmsContext<M, A>
where
M: MemoryProvider,
A: AccessControl,
{
pub fn with_acl(memory: M) -> Self {
let mut mm = MemoryManager::init(memory);
let schema_registry = SchemaRegistry::load(&mut mm).unwrap_or_default();
let acl = A::load(&mut mm).unwrap_or_default();
Self {
mm: RefCell::new(mm),
schema_registry: RefCell::new(schema_registry),
acl: RefCell::new(acl),
transaction_session: RefCell::new(TransactionSession::default()),
journal: RefCell::new(None),
drift: Cell::new(None),
migrating: Cell::new(false),
}
}
pub fn register_table<T: wasm_dbms_api::prelude::TableSchema>(
&self,
) -> DbmsResult<TableRegistryPage> {
let mut sr = self.schema_registry.borrow_mut();
let mut mm = self.mm.borrow_mut();
sr.register_table::<T>(&mut *mm).map_err(Into::into)
}
pub fn has_table(&self, name: &str) -> bool {
self.schema_registry
.borrow()
.table_registry_page_by_name(name)
.is_some()
}
pub fn granted(&self, id: &A::Id, table: TableFingerprint, required: TablePerms) -> bool {
self.acl.borrow().granted(id, table, required)
}
pub fn granted_admin(&self, id: &A::Id) -> bool {
self.acl.borrow().granted_admin(id)
}
pub fn granted_manage_acl(&self, id: &A::Id) -> bool {
self.acl.borrow().granted_manage_acl(id)
}
pub fn granted_migrate(&self, id: &A::Id) -> bool {
self.acl.borrow().granted_migrate(id)
}
pub fn acl_grant(&self, id: A::Id, grant: PermGrant) -> DbmsResult<()> {
let mut acl = self.acl.borrow_mut();
let mut mm = self.mm.borrow_mut();
acl.grant(id, grant, &mut mm).map_err(Into::into)
}
pub fn acl_revoke(&self, id: &A::Id, revoke: PermRevoke) -> DbmsResult<()> {
let mut acl = self.acl.borrow_mut();
let mut mm = self.mm.borrow_mut();
acl.revoke(id, revoke, &mut mm).map_err(Into::into)
}
pub fn acl_remove_identity(&self, id: &A::Id) -> DbmsResult<()> {
let mut acl = self.acl.borrow_mut();
let mut mm = self.mm.borrow_mut();
acl.remove_identity(id, &mut mm).map_err(Into::into)
}
pub fn acl_perms(&self, id: &A::Id) -> IdentityPerms {
self.acl.borrow().perms(id)
}
pub fn acl_identities(&self) -> Vec<(A::Id, IdentityPerms)> {
self.acl.borrow().identities()
}
pub fn begin_transaction(&self, owner: Vec<u8>) -> TransactionId {
let mut ts = self.transaction_session.borrow_mut();
ts.begin_transaction(owner)
}
pub fn has_transaction(&self, tx_id: &TransactionId, caller: &[u8]) -> bool {
let ts = self.transaction_session.borrow();
ts.has_transaction(tx_id, caller)
}
pub(crate) fn cached_drift_for(&self, compiled_hash: u64) -> Option<bool> {
self.drift
.get()
.and_then(|(hash, drifted)| (hash == compiled_hash).then_some(drifted))
}
pub(crate) fn set_drift(&self, compiled_hash: u64, value: bool) {
self.drift.set(Some((compiled_hash, value)));
}
pub(crate) fn clear_drift(&self) {
self.drift.set(None);
}
pub(crate) fn is_migrating(&self) -> bool {
self.migrating.get()
}
pub(crate) fn set_migrating(&self, value: bool) {
self.migrating.set(value);
}
}
impl<M, A> std::fmt::Debug for DbmsContext<M, A>
where
M: MemoryProvider,
A: AccessControl + std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DbmsContext")
.field("schema_registry", &self.schema_registry)
.field("acl", &self.acl)
.field("transaction_session", &self.transaction_session)
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use wasm_dbms_memory::prelude::HeapMemoryProvider;
use super::*;
#[test]
fn test_should_create_context() {
let ctx = DbmsContext::new(HeapMemoryProvider::default());
assert!(ctx.acl_identities().is_empty());
}
#[test]
fn test_should_grant_admin_to_identity() {
let ctx = DbmsContext::new(HeapMemoryProvider::default());
ctx.acl_grant(vec![1, 2, 3], PermGrant::Admin).unwrap();
assert!(ctx.granted_admin(&vec![1, 2, 3]));
assert!(!ctx.granted_admin(&vec![4, 5, 6]));
}
#[test]
fn test_should_remove_identity() {
let ctx = DbmsContext::new(HeapMemoryProvider::default());
ctx.acl_grant(vec![1, 2, 3], PermGrant::ManageAcl).unwrap();
ctx.acl_grant(vec![4, 5, 6], PermGrant::Admin).unwrap();
ctx.acl_remove_identity(&vec![4, 5, 6]).unwrap();
assert!(!ctx.granted_admin(&vec![4, 5, 6]));
assert!(ctx.granted_manage_acl(&vec![1, 2, 3]));
}
#[test]
fn test_should_begin_transaction() {
let ctx = DbmsContext::new(HeapMemoryProvider::default());
let owner = vec![1, 2, 3];
let tx_id = ctx.begin_transaction(owner.clone());
assert!(ctx.has_transaction(&tx_id, &owner));
assert!(!ctx.has_transaction(&tx_id, &[4, 5, 6]));
}
#[test]
fn test_should_debug_context() {
let ctx = DbmsContext::new(HeapMemoryProvider::default());
let debug = format!("{ctx:?}");
assert!(debug.contains("DbmsContext"));
}
}