use crate::{
AutoStorableHint,
Packed,
StorableHint,
StorageKey,
};
use core::{
fmt::Debug,
marker::PhantomData,
};
use ink_primitives::{
Key,
KeyComposer,
};
trait KeyType {
const IS_AUTO_KEY: bool;
}
#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub struct AutoKey;
impl StorageKey for AutoKey {
const KEY: Key = 0;
}
impl KeyType for AutoKey {
const IS_AUTO_KEY: bool = true;
}
impl Debug for AutoKey {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("AutoKey")
.field("key", &<Self as StorageKey>::KEY)
.finish()
}
}
#[derive(Default, Copy, Clone, Eq, PartialEq, PartialOrd)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub struct ManualKey<const KEY: Key, ParentKey: StorageKey = ()>(
PhantomData<fn() -> ParentKey>,
);
impl<const KEY: Key, ParentKey: StorageKey> StorageKey for ManualKey<KEY, ParentKey> {
const KEY: Key = KeyComposer::concat(KEY, ParentKey::KEY);
}
impl<const KEY: Key, ParentKey: StorageKey> KeyType for ManualKey<KEY, ParentKey> {
const IS_AUTO_KEY: bool = false;
}
impl<const KEY: Key, ParentKey: StorageKey> Debug for ManualKey<KEY, ParentKey> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("ManualKey")
.field("key", &<Self as StorageKey>::KEY)
.finish()
}
}
#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Debug)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub struct ResolverKey<L, R>(PhantomData<fn() -> (L, R)>);
impl<L, R> StorageKey for ResolverKey<L, R>
where
L: StorageKey + KeyType,
R: StorageKey + KeyType,
{
const KEY: Key = if L::IS_AUTO_KEY { R::KEY } else { L::KEY };
}
impl<L, R> KeyType for ResolverKey<L, R>
where
L: KeyType,
R: KeyType,
{
const IS_AUTO_KEY: bool = L::IS_AUTO_KEY;
}
type FinalKey<T, const KEY: Key, ParentKey> =
ResolverKey<<T as StorableHint<ParentKey>>::PreferredKey, ManualKey<KEY, ParentKey>>;
impl<T, const KEY: Key, ParentKey> AutoStorableHint<ManualKey<KEY, ParentKey>> for T
where
T: StorableHint<ParentKey>,
<T as StorableHint<ParentKey>>::PreferredKey: KeyType,
T: StorableHint<FinalKey<T, KEY, ParentKey>>,
ParentKey: StorageKey,
{
type Type = <T as StorableHint<FinalKey<T, KEY, ParentKey>>>::Type;
}
impl<P> super::storage::private::Sealed for P where P: scale::Decode + scale::Encode {}
impl<P> Packed for P where P: scale::Decode + scale::Encode {}
impl<P> StorageKey for P
where
P: Packed,
{
const KEY: Key = 0;
}
impl<P, Key> StorableHint<Key> for P
where
P: Packed,
Key: StorageKey,
{
type Type = P;
type PreferredKey = AutoKey;
}
#[cfg(test)]
mod tests {
use super::*;
#[macro_export]
macro_rules! storage_hint_works_for_primitive {
( $ty:ty ) => {
paste::item! {
#[test]
#[allow(non_snake_case)]
fn [<$ty _storage_hint_works>] () {
assert_eq!(
::core::any::TypeId::of::<$ty>(),
::core::any::TypeId::of::<<$ty as $crate::StorableHint<$crate::ManualKey<123>>>::Type>()
);
}
}
};
}
mod arrays {
use crate::storage_hint_works_for_primitive;
type Array = [i32; 4];
storage_hint_works_for_primitive!(Array);
type ArrayTuples = [(i32, i32); 2];
storage_hint_works_for_primitive!(ArrayTuples);
}
mod prims {
use crate::storage_hint_works_for_primitive;
use ink_primitives::AccountId;
storage_hint_works_for_primitive!(bool);
storage_hint_works_for_primitive!(String);
storage_hint_works_for_primitive!(AccountId);
storage_hint_works_for_primitive!(i8);
storage_hint_works_for_primitive!(i16);
storage_hint_works_for_primitive!(i32);
storage_hint_works_for_primitive!(i64);
storage_hint_works_for_primitive!(i128);
storage_hint_works_for_primitive!(u8);
storage_hint_works_for_primitive!(u16);
storage_hint_works_for_primitive!(u32);
storage_hint_works_for_primitive!(u64);
storage_hint_works_for_primitive!(u128);
type OptionU8 = Option<u8>;
storage_hint_works_for_primitive!(OptionU8);
type ResultU8 = Result<u8, bool>;
storage_hint_works_for_primitive!(ResultU8);
type BoxU8 = Box<u8>;
storage_hint_works_for_primitive!(BoxU8);
type BoxOptionU8 = Box<Option<u8>>;
storage_hint_works_for_primitive!(BoxOptionU8);
}
mod tuples {
use crate::storage_hint_works_for_primitive;
type TupleSix = (i32, u32, String, u8, bool, Box<Option<i32>>);
storage_hint_works_for_primitive!(TupleSix);
}
#[test]
fn storage_key_types_works() {
assert_eq!(<AutoKey as StorageKey>::KEY, 0);
assert_eq!(<ManualKey<123> as StorageKey>::KEY, 123);
assert_eq!(<ManualKey<0> as StorageKey>::KEY, 0);
assert_eq!(<ResolverKey<AutoKey, AutoKey> as StorageKey>::KEY, 0);
assert_eq!(
<ResolverKey<AutoKey, ManualKey<123>> as StorageKey>::KEY,
123
);
assert_eq!(
<ResolverKey<ManualKey<456>, ManualKey<123>> as StorageKey>::KEY,
456
);
assert_eq!(
<ResolverKey<ManualKey<0>, ManualKey<123>> as StorageKey>::KEY,
0
);
}
}