ink_storage_traits/impls/
mod.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::{
16    AutoStorableHint,
17    Packed,
18    StorableHint,
19    StorageKey,
20};
21use core::{
22    fmt::Debug,
23    marker::PhantomData,
24};
25use ink_primitives::{
26    Key,
27    KeyComposer,
28};
29
30/// The private trait helping identify the [`AutoKey`] key type.
31trait KeyType {
32    /// It is `true` for [`AutoKey`] and `false` for [`ManualKey`].
33    /// It helps the [`ResolverKey`] select between the user-specified (left key)
34    /// and the auto-generated (right key) keys.
35    const IS_AUTO_KEY: bool;
36}
37
38/// Auto key type means that the storage key should be calculated automatically.
39#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd)]
40#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
41pub struct AutoKey;
42
43impl StorageKey for AutoKey {
44    const KEY: Key = 0;
45}
46
47impl KeyType for AutoKey {
48    const IS_AUTO_KEY: bool = true;
49}
50
51impl Debug for AutoKey {
52    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
53        f.debug_struct("AutoKey")
54            .field("key", &<Self as StorageKey>::KEY)
55            .finish()
56    }
57}
58
59/// Manual key type specifies the storage key.
60#[derive(Default, Copy, Clone, Eq, PartialEq, PartialOrd)]
61#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
62pub struct ManualKey<const KEY: Key, ParentKey: StorageKey = ()>(
63    PhantomData<fn() -> ParentKey>,
64);
65
66impl<const KEY: Key, ParentKey: StorageKey> StorageKey for ManualKey<KEY, ParentKey> {
67    const KEY: Key = KeyComposer::concat(KEY, ParentKey::KEY);
68}
69
70impl<const KEY: Key, ParentKey: StorageKey> KeyType for ManualKey<KEY, ParentKey> {
71    const IS_AUTO_KEY: bool = false;
72}
73
74impl<const KEY: Key, ParentKey: StorageKey> Debug for ManualKey<KEY, ParentKey> {
75    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
76        f.debug_struct("ManualKey")
77            .field("key", &<Self as StorageKey>::KEY)
78            .finish()
79    }
80}
81
82/// Resolver key type selects between preferred key and autogenerated key.
83/// If the `L` type is `AutoKey` it returns auto-generated `R` else `L`.
84#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Debug)]
85#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
86pub struct ResolverKey<L, R>(PhantomData<fn() -> (L, R)>);
87
88impl<L, R> StorageKey for ResolverKey<L, R>
89where
90    L: StorageKey + KeyType,
91    R: StorageKey + KeyType,
92{
93    /// If the left key is [`AutoKey`], then use the right auto-generated storage key.
94    /// Otherwise use the left [`ManualKey`].
95    const KEY: Key = if L::IS_AUTO_KEY { R::KEY } else { L::KEY };
96}
97
98impl<L, R> KeyType for ResolverKey<L, R>
99where
100    L: KeyType,
101    R: KeyType,
102{
103    /// The right key is always an auto-generated key, the user can specify only the left
104    /// key. So the left key defines the [`KeyType::IS_AUTO_KEY`] of the
105    /// [`ResolverKey`].
106    const IS_AUTO_KEY: bool = L::IS_AUTO_KEY;
107}
108
109type FinalKey<T, const KEY: Key, ParentKey> =
110    ResolverKey<<T as StorableHint<ParentKey>>::PreferredKey, ManualKey<KEY, ParentKey>>;
111
112// `AutoStorableHint` trait figures out that storage key it should use.
113// - If the `PreferredKey` is `AutoKey` it will use an auto-generated key passed as
114//   generic
115// into `AutoStorableHint`.
116// - If `PreferredKey` is `ManualKey`, then it will use it.
117impl<T, const KEY: Key, ParentKey> AutoStorableHint<ManualKey<KEY, ParentKey>> for T
118where
119    T: StorableHint<ParentKey>,
120    <T as StorableHint<ParentKey>>::PreferredKey: KeyType,
121    T: StorableHint<FinalKey<T, KEY, ParentKey>>,
122    ParentKey: StorageKey,
123{
124    type Type = <T as StorableHint<FinalKey<T, KEY, ParentKey>>>::Type;
125}
126
127impl<P> super::storage::private::Sealed for P where P: scale::Decode + scale::Encode {}
128#[diagnostic::do_not_recommend]
129impl<P> Packed for P where P: scale::Decode + scale::Encode {}
130
131impl<P> StorageKey for P
132where
133    P: Packed,
134{
135    const KEY: Key = 0;
136}
137
138impl<P, Key> StorableHint<Key> for P
139where
140    P: Packed,
141    Key: StorageKey,
142{
143    type Type = P;
144    type PreferredKey = AutoKey;
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150
151    /// Creates test to verify that the primitive types are packed.
152    #[macro_export]
153    macro_rules! storage_hint_works_for_primitive {
154        ( $ty:ty ) => {
155            paste::item! {
156                #[test]
157                #[allow(non_snake_case)]
158                fn [<$ty _storage_hint_works>] () {
159                    assert_eq!(
160                        ::core::any::TypeId::of::<$ty>(),
161                        ::core::any::TypeId::of::<<$ty as $crate::StorableHint<$crate::ManualKey<123>>>::Type>()
162                    );
163                }
164            }
165        };
166    }
167    mod arrays {
168        use crate::storage_hint_works_for_primitive;
169
170        type Array = [i32; 4];
171        storage_hint_works_for_primitive!(Array);
172
173        type ArrayTuples = [(i32, i32); 2];
174        storage_hint_works_for_primitive!(ArrayTuples);
175    }
176
177    mod prims {
178        use crate::storage_hint_works_for_primitive;
179        use ink_primitives::AccountId;
180
181        storage_hint_works_for_primitive!(bool);
182        storage_hint_works_for_primitive!(String);
183        storage_hint_works_for_primitive!(AccountId);
184        storage_hint_works_for_primitive!(i8);
185        storage_hint_works_for_primitive!(i16);
186        storage_hint_works_for_primitive!(i32);
187        storage_hint_works_for_primitive!(i64);
188        storage_hint_works_for_primitive!(i128);
189        storage_hint_works_for_primitive!(u8);
190        storage_hint_works_for_primitive!(u16);
191        storage_hint_works_for_primitive!(u32);
192        storage_hint_works_for_primitive!(u64);
193        storage_hint_works_for_primitive!(u128);
194
195        type OptionU8 = Option<u8>;
196        storage_hint_works_for_primitive!(OptionU8);
197
198        type ResultU8 = Result<u8, bool>;
199        storage_hint_works_for_primitive!(ResultU8);
200
201        type BoxU8 = Box<u8>;
202        storage_hint_works_for_primitive!(BoxU8);
203
204        type BoxOptionU8 = Box<Option<u8>>;
205        storage_hint_works_for_primitive!(BoxOptionU8);
206    }
207
208    mod tuples {
209        use crate::storage_hint_works_for_primitive;
210
211        type TupleSix = (i32, u32, String, u8, bool, Box<Option<i32>>);
212        storage_hint_works_for_primitive!(TupleSix);
213    }
214
215    #[test]
216    fn storage_key_types_works() {
217        assert_eq!(<AutoKey as StorageKey>::KEY, 0);
218        assert_eq!(<ManualKey<123> as StorageKey>::KEY, 123);
219        assert_eq!(<ManualKey<0> as StorageKey>::KEY, 0);
220        assert_eq!(<ResolverKey<AutoKey, AutoKey> as StorageKey>::KEY, 0);
221        assert_eq!(
222            <ResolverKey<AutoKey, ManualKey<123>> as StorageKey>::KEY,
223            123
224        );
225        assert_eq!(
226            <ResolverKey<ManualKey<456>, ManualKey<123>> as StorageKey>::KEY,
227            456
228        );
229        assert_eq!(
230            <ResolverKey<ManualKey<0>, ManualKey<123>> as StorageKey>::KEY,
231            0
232        );
233    }
234}