use super::{StorageClearable, StorageMapper};
use crate::abi::{TypeAbi, TypeDescriptionContainer, TypeName};
use crate::api::{EndpointFinishApi, ErrorApi, StorageReadApi, StorageWriteApi};
use crate::io::EndpointResult;
use crate::storage::{storage_get, storage_set};
use crate::types::{BoxedBytes, MultiResultVec};
use alloc::vec::Vec;
use core::{marker::PhantomData, usize};
use dharitri_codec::{TopDecode, TopEncode};
const ITEM_SUFFIX: &[u8] = b".item";
const LEN_SUFFIX: &[u8] = b".len";
fn compute_item_key(prefix: &[u8], index: usize) -> BoxedBytes {
let index_bytes = (index as u32).to_be_bytes();
BoxedBytes::from_concat(&[prefix, ITEM_SUFFIX, &index_bytes[..]])
}
pub struct VecMapper<SA, T>
where
SA: StorageReadApi + StorageWriteApi + ErrorApi + Clone + 'static,
T: TopEncode + TopDecode + 'static,
{
api: SA,
main_key: BoxedBytes,
_phantom: core::marker::PhantomData<T>,
}
impl<SA, T> StorageMapper<SA> for VecMapper<SA, T>
where
SA: StorageReadApi + StorageWriteApi + ErrorApi + Clone + 'static,
T: TopEncode + TopDecode,
{
fn new(api: SA, main_key: BoxedBytes) -> Self {
VecMapper {
api,
main_key,
_phantom: PhantomData,
}
}
}
impl<SA, T> StorageClearable for VecMapper<SA, T>
where
SA: StorageReadApi + StorageWriteApi + ErrorApi + Clone + 'static,
T: TopEncode + TopDecode,
{
fn clear(&mut self) {
self.clear();
}
}
impl<SA, T> VecMapper<SA, T>
where
SA: StorageReadApi + StorageWriteApi + ErrorApi + Clone + 'static,
T: TopEncode + TopDecode,
{
fn item_key(&self, index: usize) -> BoxedBytes {
compute_item_key(self.main_key.as_slice(), index)
}
fn len_key(&self) -> BoxedBytes {
BoxedBytes::from_concat(&[self.main_key.as_slice(), LEN_SUFFIX])
}
fn save_count(&self, new_len: usize) {
storage_set(self.api.clone(), self.len_key().as_slice(), &new_len);
}
pub fn len(&self) -> usize {
storage_get(self.api.clone(), self.len_key().as_slice())
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn push(&mut self, item: &T) -> usize {
let mut len = self.len();
len += 1;
storage_set(self.api.clone(), self.item_key(len).as_slice(), item);
self.save_count(len);
len
}
pub fn extend_from_slice(&mut self, items: &[T]) -> usize {
let mut len = self.len();
for item in items {
len += 1;
storage_set(self.api.clone(), self.item_key(len).as_slice(), item);
}
self.save_count(len);
len
}
pub fn get(&self, index: usize) -> T {
if index == 0 || index > self.len() {
self.api.signal_error(&b"index out of range"[..]);
}
self.get_unchecked(index)
}
pub fn get_unchecked(&self, index: usize) -> T {
storage_get(self.api.clone(), self.item_key(index).as_slice())
}
pub fn get_or_else<F: FnOnce() -> T>(self, index: usize, or_else: F) -> T {
if index == 0 || index > self.len() {
or_else()
} else {
self.get_unchecked(index)
}
}
pub fn item_is_empty_unchecked(&self, index: usize) -> bool {
self.api.storage_load_len(self.item_key(index).as_slice()) == 0
}
pub fn item_is_empty(&self, index: usize) -> bool {
if index == 0 || index > self.len() {
self.api.signal_error(&b"index out of range"[..]);
}
self.item_is_empty_unchecked(index)
}
pub fn set(&self, index: usize, item: &T) {
if index == 0 || index > self.len() {
self.api.signal_error(&b"index out of range"[..]);
}
self.set_unchecked(index, item);
}
fn set_unchecked(&self, index: usize, item: &T) {
storage_set(self.api.clone(), self.item_key(index).as_slice(), item);
}
pub fn clear_entry(&self, index: usize) {
if index == 0 || index > self.len() {
self.api.signal_error(&b"index out of range"[..]);
}
self.clear_entry_unchecked(index)
}
pub fn clear_entry_unchecked(&self, index: usize) {
self.api
.storage_store_slice_u8(self.item_key(index).as_slice(), &[]);
}
pub fn load_as_vec(&self) -> Vec<T> {
let len = self.len();
let mut result = Vec::with_capacity(len);
for i in 1..=len {
result.push(self.get(i));
}
result
}
pub fn clear(&mut self) {
let len = self.len();
for i in 1..=len {
self.api
.storage_store_slice_u8(self.item_key(i).as_slice(), &[]);
}
self.save_count(0);
}
}
impl<SA, FA, T> EndpointResult<FA> for VecMapper<SA, T>
where
SA: StorageReadApi + StorageWriteApi + ErrorApi + Clone + 'static,
FA: EndpointFinishApi + Clone + 'static,
T: TopEncode + TopDecode + EndpointResult<FA>,
{
fn finish(&self, api: FA) {
let v = self.load_as_vec();
MultiResultVec::<T>::from(v).finish(api);
}
}
impl<SA, T> TypeAbi for VecMapper<SA, T>
where
SA: StorageReadApi + StorageWriteApi + ErrorApi + Clone + 'static,
T: TopEncode + TopDecode + TypeAbi,
{
fn type_name() -> TypeName {
crate::types::MultiResultVec::<T>::type_name()
}
fn provide_type_descriptions<TDC: TypeDescriptionContainer>(accumulator: &mut TDC) {
T::provide_type_descriptions(accumulator);
}
fn is_multi_arg_or_result() -> bool {
true
}
}