use tet_core::offchain::StorageKind;
pub type StorageValue = StorageValueRef<'static>;
pub struct StorageValueRef<'a> {
key: &'a [u8],
kind: StorageKind,
}
impl<'a> StorageValueRef<'a> {
pub fn persistent(key: &'a [u8]) -> Self {
Self { key, kind: StorageKind::PERSISTENT }
}
pub fn local(key: &'a [u8]) -> Self {
Self { key, kind: StorageKind::LOCAL }
}
pub fn set(&self, value: &impl codec::Encode) {
value.using_encoded(|val| {
tet_io::offchain::local_storage_set(self.kind, self.key, val)
})
}
pub fn clear(&mut self) {
tet_io::offchain::local_storage_clear(self.kind, self.key)
}
pub fn get<T: codec::Decode>(&self) -> Option<Option<T>> {
tet_io::offchain::local_storage_get(self.kind, self.key)
.map(|val| T::decode(&mut &*val).ok())
}
pub fn mutate<T, E, F>(&self, f: F) -> Result<Result<T, T>, E> where
T: codec::Codec,
F: FnOnce(Option<Option<T>>) -> Result<T, E>
{
let value = tet_io::offchain::local_storage_get(self.kind, self.key);
let decoded = value.as_deref().map(|mut v| T::decode(&mut v).ok());
let val = f(decoded)?;
let set = val.using_encoded(|new_val| {
tet_io::offchain::local_storage_compare_and_set(
self.kind,
self.key,
value,
new_val,
)
});
if set {
Ok(Ok(val))
} else {
Ok(Err(val))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use tet_io::TestExternalities;
use tet_core::offchain::{
OffchainExt,
testing,
};
#[test]
fn should_set_and_get() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.execute_with(|| {
let val = StorageValue::persistent(b"testval");
assert_eq!(val.get::<u32>(), None);
val.set(&15_u32);
assert_eq!(val.get::<u32>(), Some(Some(15_u32)));
assert_eq!(val.get::<Vec<u8>>(), Some(None));
assert_eq!(
state.read().persistent_storage.get(b"testval"),
Some(vec![15_u8, 0, 0, 0])
);
})
}
#[test]
fn should_mutate() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.execute_with(|| {
let val = StorageValue::persistent(b"testval");
let result = val.mutate::<u32, (), _>(|val| {
assert_eq!(val, None);
Ok(16_u32)
});
assert_eq!(result, Ok(Ok(16_u32)));
assert_eq!(val.get::<u32>(), Some(Some(16_u32)));
assert_eq!(
state.read().persistent_storage.get(b"testval"),
Some(vec![16_u8, 0, 0, 0])
);
let res = val.mutate::<u32, (), _>(|val| {
assert_eq!(val, Some(Some(16_u32)));
Err(())
});
assert_eq!(res, Err(()));
})
}
}