#[cfg(not(feature = "mock-storage"))]
use soroban_sdk::storage::{Instance, Persistent, Temporary};
#[cfg(feature = "mock-storage")]
pub use crate::test_utils::{
with_instance_storage, with_persistent_storage, with_temporary_storage,
};
use soroban_sdk::{Env, IntoVal, TryFromVal, Val};
use core::marker::PhantomData;
#[inline]
#[cfg(not(feature = "mock-storage"))]
pub fn with_instance_storage<F, T>(env: &Env, f: F) -> T
where
F: FnOnce(&Instance) -> T,
{
f(&env.storage().instance())
}
#[inline]
#[cfg(not(feature = "mock-storage"))]
pub fn with_persistent_storage<F, T>(env: &Env, f: F) -> T
where
F: FnOnce(&Persistent) -> T,
{
f(&env.storage().persistent())
}
#[inline]
#[cfg(not(feature = "mock-storage"))]
pub fn with_temporary_storage<F, T>(env: &Env, f: F) -> T
where
F: FnOnce(&Temporary) -> T,
{
f(&env.storage().temporary())
}
pub struct StorageProxy<'a, K, T>
where
K: 'a + IntoVal<Env, Val> + TryFromVal<Env, Val>,
{
key: &'a K,
_data: PhantomData<*const T>,
}
impl<'a, K, T> StorageProxy<'a, K, T>
where
K: IntoVal<Env, Val> + TryFromVal<Env, Val>,
{
fn new(key: &'a K) -> Self {
StorageProxy {
key,
_data: PhantomData,
}
}
pub fn get_key(&self) -> &'a K {
self.key
}
}
pub trait StorageOps<T> {
fn get(&self, env: &Env) -> Option<T>;
fn set(&self, env: &Env, data: &T);
fn remove(&self, env: &Env);
fn has(&self, env: &Env) -> bool;
fn bump(&self, env: &Env, low_expiration_watermark: u64, hi_expiration_watermark: u64);
}
pub fn get<'a, K, T>(env: &Env, key: &'a K) -> Option<T>
where
StorageProxy<'a, K, T>: StorageOps<T>,
K: IntoVal<Env, Val> + TryFromVal<Env, Val> + ?Sized,
{
StorageProxy::<'a, K, T>::new(key).get(env)
}
pub fn get_or_else<'a, K, T, F, R>(env: &Env, key: &'a K, handler: F) -> R
where
StorageProxy<'a, K, T>: StorageOps<T>,
K: IntoVal<Env, Val> + TryFromVal<Env, Val> + ?Sized,
F: FnOnce(Option<T>) -> R,
{
handler(StorageProxy::<'a, K, T>::new(key).get(env))
}
pub fn set<'a, K, T>(env: &Env, key: &'a K, data: &T)
where
StorageProxy<'a, K, T>: StorageOps<T>,
K: IntoVal<Env, Val> + TryFromVal<Env, Val> + ?Sized,
{
StorageProxy::<'a, K, T>::new(key).set(env, data);
}
pub fn has<'a, K, T>(env: &Env, key: &'a K) -> bool
where
StorageProxy<'a, K, T>: StorageOps<T>,
K: IntoVal<Env, Val> + TryFromVal<Env, Val> + ?Sized,
{
StorageProxy::<'a, K, T>::new(key).has(env)
}
pub fn remove<'a, K, T>(env: &Env, key: &'a K)
where
StorageProxy<'a, K, T>: StorageOps<T>,
K: IntoVal<Env, Val> + TryFromVal<Env, Val> + ?Sized,
{
StorageProxy::<'a, K, T>::new(key).remove(env);
}
pub fn bump<'a, K, T>(
env: &Env,
key: &'a K,
low_expiration_watermark: u64,
hi_expiration_watermark: u64,
) where
StorageProxy<'a, K, T>: StorageOps<T>,
K: IntoVal<Env, Val> + TryFromVal<Env, Val> + ?Sized,
{
StorageProxy::<'a, K, T>::new(key).bump(env, low_expiration_watermark, hi_expiration_watermark);
}
#[macro_export]
macro_rules! impl_key_constraint {
($key_type:ty, $key_trait:ident) => {
pub trait $key_trait {}
impl $key_trait for $key_type {}
};
}
#[macro_export]
macro_rules! impl_storage {
(Instance, $data_type:ty $(, $key_trait:ident)?) => {
impl<'a, K> $crate::storage::StorageOps<$data_type>
for $crate::storage::StorageProxy<'a, K, $data_type>
where
K: $( $key_trait + )? soroban_sdk::IntoVal<soroban_sdk::Env, soroban_sdk::Val>
+ soroban_sdk::TryFromVal<soroban_sdk::Env, soroban_sdk::Val>,
{
fn get(&self, env: &soroban_sdk::Env) -> Option<$data_type> {
$crate::storage::with_instance_storage(env, |storage| storage.get(self.get_key()))
}
fn set(&self, env: &soroban_sdk::Env, data: &$data_type) {
$crate::storage::with_instance_storage(env, |storage| {
storage.set(self.get_key(), data)
});
}
fn remove(&self, env: &soroban_sdk::Env) {
$crate::storage::with_instance_storage(env, |storage| {
storage.remove(self.get_key())
});
}
fn has(&self, env: &soroban_sdk::Env) -> bool {
$crate::storage::with_instance_storage(env, |storage| storage.has(self.get_key()))
}
fn bump(&self, env: &Env, low_expiration_watermark: u64, hi_expiration_watermark: u64) {
$crate::storage::with_instance_storage(env, |storage| {
storage.bump(
low_expiration_watermark as u32,
hi_expiration_watermark as u32,
)
});
}
}
};
(Persistent, $data_type:ty $(, $key_type:ident)?) => {
impl<'a, K> $crate::storage::StorageOps<$data_type>
for $crate::storage::StorageProxy<'a, K, $data_type>
where
K: $( $key_type + )? soroban_sdk::IntoVal<soroban_sdk::Env, soroban_sdk::Val>
+ soroban_sdk::TryFromVal<soroban_sdk::Env, soroban_sdk::Val>,
{
fn get(&self, env: &soroban_sdk::Env) -> Option<$data_type> {
$crate::storage::with_persistent_storage(env, |storage| storage.get(self.get_key()))
}
fn set(&self, env: &soroban_sdk::Env, data: &$data_type) {
$crate::storage::with_persistent_storage(env, |storage| {
storage.set(self.get_key(), data)
});
}
fn remove(&self, env: &soroban_sdk::Env) {
$crate::storage::with_persistent_storage(env, |storage| {
storage.remove(self.get_key())
});
}
fn has(&self, env: &soroban_sdk::Env) -> bool {
$crate::storage::with_persistent_storage(env, |storage| storage.has(self.get_key()))
}
fn bump(&self, env: &Env, low_expiration_watermark: u64, hi_expiration_watermark: u64) {
$crate::storage::with_persistent_storage(env, |storage| {
storage.bump(
self.get_key(),
low_expiration_watermark as u32,
hi_expiration_watermark as u32,
)
});
}
}
};
(Temporary, $data_type:ty $(, $key_type:ident)?) => {
impl<'a, K> $crate::storage::StorageOps<$data_type>
for $crate::storage::StorageProxy<'a, K, $data_type>
where
K: $( $key_type + )? soroban_sdk::IntoVal<soroban_sdk::Env, soroban_sdk::Val>
+ soroban_sdk::TryFromVal<soroban_sdk::Env, soroban_sdk::Val>,
{
fn get(&self, env: &soroban_sdk::Env) -> Option<$data_type> {
$crate::storage::with_temporary_storage(env, |storage| storage.get(self.get_key()))
}
fn set(&self, env: &soroban_sdk::Env, data: &$data_type) {
$crate::storage::with_temporary_storage(env, |storage| {
storage.set(self.get_key(), data)
});
}
fn remove(&self, env: &soroban_sdk::Env) {
$crate::storage::with_temporary_storage(env, |storage| {
storage.remove(self.get_key())
});
}
fn has(&self, env: &soroban_sdk::Env) -> bool {
$crate::storage::with_temporary_storage(env, |storage| storage.has(self.get_key()))
}
fn bump(&self, env: &Env, low_expiration_watermark: u64, hi_expiration_watermark: u64) {
$crate::storage::with_temporary_storage(env, |storage| {
storage.bump(
self.get_key(),
low_expiration_watermark as u32,
hi_expiration_watermark as u32,
)
});
}
}
};
}