use super::StorageMapper;
use crate::abi::{TypeAbi, TypeName};
use crate::api::{EndpointFinishApi, ErrorApi, StorageReadApi, StorageWriteApi};
use crate::io::EndpointResult;
use crate::storage::{storage_get, storage_set};
use crate::types::{Address, BoxedBytes, MultiResultVec};
use alloc::vec::Vec;
const ADDRESS_TO_ID_SUFFIX: &[u8] = b"_address_to_id";
const ID_TO_ADDRESS_SUFFIX: &[u8] = b"_id_to_address";
const COUNT_SUFFIX: &[u8] = b"_count";
pub struct UserMapper<SA>
where
SA: StorageReadApi + StorageWriteApi + ErrorApi + Clone + 'static,
{
api: SA,
main_key: BoxedBytes,
}
impl<SA> StorageMapper<SA> for UserMapper<SA>
where
SA: StorageReadApi + StorageWriteApi + ErrorApi + Clone + 'static,
{
fn new(api: SA, main_key: BoxedBytes) -> Self {
UserMapper { api, main_key }
}
}
impl<SA> UserMapper<SA>
where
SA: StorageReadApi + StorageWriteApi + ErrorApi + Clone + 'static,
{
fn get_user_id_key(&self, address: &Address) -> BoxedBytes {
BoxedBytes::from_concat(&[
self.main_key.as_slice(),
ADDRESS_TO_ID_SUFFIX,
address.as_bytes(),
])
}
pub fn get_user_id(&self, address: &Address) -> usize {
storage_get(self.api.clone(), self.get_user_id_key(address).as_slice())
}
fn set_user_id(&self, address: &Address, id: usize) {
storage_set(
self.api.clone(),
self.get_user_id_key(address).as_slice(),
&id,
);
}
fn get_user_address_key(&self, id: usize) -> BoxedBytes {
let id_bytes = (id as u32).to_be_bytes();
BoxedBytes::from_concat(&[
self.main_key.as_slice(),
ID_TO_ADDRESS_SUFFIX,
&id_bytes[..],
])
}
pub fn get_user_address(&self, id: usize) -> Option<Address> {
let key = self.get_user_address_key(id);
if self.api.storage_load_len(key.as_slice()) > 0 {
Some(storage_get(self.api.clone(), key.as_slice()))
} else {
None
}
}
pub fn get_user_address_unchecked(&self, id: usize) -> Address {
let key = self.get_user_address_key(id);
storage_get(self.api.clone(), key.as_slice())
}
pub fn get_user_address_or_zero(&self, id: usize) -> Address {
let key = self.get_user_address_key(id);
if self.api.storage_load_len(key.as_slice()) > 0 {
storage_get(self.api.clone(), key.as_slice())
} else {
Address::zero()
}
}
fn set_user_address(&self, id: usize, address: &Address) {
storage_set(
self.api.clone(),
self.get_user_address_key(id).as_slice(),
address,
);
}
fn get_user_count_key(&self) -> BoxedBytes {
BoxedBytes::from_concat(&[self.main_key.as_slice(), COUNT_SUFFIX])
}
pub fn get_user_count(&self) -> usize {
storage_get(self.api.clone(), self.get_user_count_key().as_slice())
}
fn set_user_count(&self, user_count: usize) {
storage_set(
self.api.clone(),
self.get_user_count_key().as_slice(),
&user_count,
);
}
pub fn get_or_create_user(&self, address: &Address) -> usize {
let mut user_id = self.get_user_id(&address);
if user_id == 0 {
let mut user_count = self.get_user_count();
user_count += 1;
self.set_user_count(user_count);
user_id = user_count;
self.set_user_id(&address, user_id);
self.set_user_address(user_id, &address);
}
user_id
}
pub fn get_or_create_users<F: FnMut(usize, bool)>(
&self,
addresses: &[Address],
mut user_id_lambda: F,
) {
let mut user_count = self.get_user_count();
for address in addresses {
let mut user_id = self.get_user_id(&address);
if user_id > 0 {
user_id_lambda(user_id, false);
} else {
user_count += 1;
user_id = user_count;
self.set_user_id(&address, user_id);
self.set_user_address(user_id, &address);
user_id_lambda(user_id, true);
}
}
self.set_user_count(user_count);
}
pub fn get_all_addresses(&self) -> Vec<Address> {
let user_count = self.get_user_count();
let mut result = Vec::with_capacity(user_count);
for i in 1..=user_count {
result.push(self.get_user_address_or_zero(i));
}
result
}
}
impl<SA, FA> EndpointResult<FA> for UserMapper<SA>
where
SA: StorageReadApi + StorageWriteApi + ErrorApi + Clone + 'static,
FA: EndpointFinishApi + ErrorApi + Clone + 'static,
{
fn finish(&self, api: FA) {
let addr_vec = self.get_all_addresses();
MultiResultVec::<Address>::from(addr_vec).finish(api);
}
}
impl<SA> TypeAbi for UserMapper<SA>
where
SA: StorageReadApi + StorageWriteApi + ErrorApi + Clone + 'static,
{
fn type_name() -> TypeName {
crate::types::MultiResultVec::<Address>::type_name()
}
fn is_multi_arg_or_result() -> bool {
true
}
}