use std::cell::RefCell;
use std::rc::Rc;
use calimero_primitives::context::ContextId;
use calimero_primitives::identity::PublicKey;
use calimero_storage::env::RuntimeEnv;
use calimero_storage::store::Key;
use calimero_store::{key, types, Store};
use tracing::warn;
pub fn create_runtime_env(
store: &Store,
context_id: ContextId,
executor_id: PublicKey,
) -> RuntimeEnv {
let callbacks = create_storage_callbacks(store, context_id);
RuntimeEnv::new(
callbacks.read,
callbacks.write,
callbacks.remove,
*context_id.as_ref(),
*executor_id.as_ref(),
)
}
#[expect(
clippy::type_complexity,
reason = "Matches RuntimeEnv callback signatures"
)]
struct StorageCallbacks {
read: Rc<dyn Fn(&Key) -> Option<Vec<u8>>>,
write: Rc<dyn Fn(Key, &[u8]) -> bool>,
remove: Rc<dyn Fn(&Key) -> bool>,
}
#[expect(
clippy::type_complexity,
reason = "Matches RuntimeEnv callback signatures"
)]
fn create_storage_callbacks(store: &Store, context_id: ContextId) -> StorageCallbacks {
let read: Rc<dyn Fn(&Key) -> Option<Vec<u8>>> = {
let handle = store.handle();
let ctx_id = context_id;
Rc::new(move |key: &Key| {
let storage_key = key.to_bytes();
let state_key = key::ContextState::new(ctx_id, storage_key);
match handle.get(&state_key) {
Ok(Some(state)) => Some(state.value.into_boxed().into_vec()),
Ok(None) => None,
Err(e) => {
warn!(
%ctx_id,
storage_key = %hex::encode(storage_key),
error = ?e,
"Storage read failed"
);
None
}
}
})
};
let write: Rc<dyn Fn(Key, &[u8]) -> bool> = {
let handle_cell: Rc<RefCell<_>> = Rc::new(RefCell::new(store.handle()));
let ctx_id = context_id;
Rc::new(move |key: Key, value: &[u8]| {
let storage_key = key.to_bytes();
let state_key = key::ContextState::new(ctx_id, storage_key);
let slice: calimero_store::slice::Slice<'_> = value.to_vec().into();
let state_value = types::ContextState::from(slice);
handle_cell
.borrow_mut()
.put(&state_key, &state_value)
.is_ok()
})
};
let remove: Rc<dyn Fn(&Key) -> bool> = {
let handle_cell: Rc<RefCell<_>> = Rc::new(RefCell::new(store.handle()));
let ctx_id = context_id;
Rc::new(move |key: &Key| {
let storage_key = key.to_bytes();
let state_key = key::ContextState::new(ctx_id, storage_key);
handle_cell.borrow_mut().delete(&state_key).is_ok()
})
};
StorageCallbacks {
read,
write,
remove,
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
use calimero_storage::env::with_runtime_env;
use calimero_storage::index::Index;
use calimero_storage::store::MainStorage;
use calimero_store::db::InMemoryDB;
#[test]
fn test_create_runtime_env_with_inmemory() {
let db = InMemoryDB::owned();
let store = Store::new(Arc::new(db));
let context_id = ContextId::from([1u8; 32]);
let identity = PublicKey::from([2u8; 32]);
let env = create_runtime_env(&store, context_id, identity);
let result = with_runtime_env(env, || {
Index::<MainStorage>::get_index(calimero_storage::address::Id::root())
});
assert!(result.is_ok());
}
}