1use crate::{
21 storage::{
22 generator::StorageValue as StorageValueT,
23 types::{OptionQuery, QueryKindTrait, StorageEntryMetadataBuilder},
24 StorageAppend, StorageDecodeLength, StorageTryAppend,
25 },
26 traits::{Get, GetDefault, StorageInfo, StorageInstance},
27};
28use alloc::{vec, vec::Vec};
29use codec::{Decode, Encode, EncodeLike, FullCodec, MaxEncodedLen};
30use frame_support::storage::StorageDecodeNonDedupLength;
31use sp_arithmetic::traits::SaturatedConversion;
32use sp_metadata_ir::{StorageEntryMetadataIR, StorageEntryTypeIR};
33
34pub struct StorageValue<Prefix, Value, QueryKind = OptionQuery, OnEmpty = GetDefault>(
66 core::marker::PhantomData<(Prefix, Value, QueryKind, OnEmpty)>,
67);
68
69impl<Prefix, Value, QueryKind, OnEmpty> crate::storage::generator::StorageValue<Value>
70 for StorageValue<Prefix, Value, QueryKind, OnEmpty>
71where
72 Prefix: StorageInstance,
73 Value: FullCodec,
74 QueryKind: QueryKindTrait<Value, OnEmpty>,
75 OnEmpty: Get<QueryKind::Query> + 'static,
76{
77 type Query = QueryKind::Query;
78 fn pallet_prefix() -> &'static [u8] {
79 Prefix::pallet_prefix().as_bytes()
80 }
81 fn storage_prefix() -> &'static [u8] {
82 Prefix::STORAGE_PREFIX.as_bytes()
83 }
84 fn from_optional_value_to_query(v: Option<Value>) -> Self::Query {
85 QueryKind::from_optional_value_to_query(v)
86 }
87 fn from_query_to_optional_value(v: Self::Query) -> Option<Value> {
88 QueryKind::from_query_to_optional_value(v)
89 }
90 fn storage_value_final_key() -> [u8; 32] {
91 Prefix::prefix_hash()
92 }
93}
94
95impl<Prefix, Value, QueryKind, OnEmpty> StorageValue<Prefix, Value, QueryKind, OnEmpty>
96where
97 Prefix: StorageInstance,
98 Value: FullCodec,
99 QueryKind: QueryKindTrait<Value, OnEmpty>,
100 OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
101{
102 pub fn hashed_key() -> [u8; 32] {
104 <Self as crate::storage::StorageValue<Value>>::hashed_key()
105 }
106
107 pub fn exists() -> bool {
109 <Self as crate::storage::StorageValue<Value>>::exists()
110 }
111
112 pub fn get() -> QueryKind::Query {
114 <Self as crate::storage::StorageValue<Value>>::get()
115 }
116
117 pub fn try_get() -> Result<Value, ()> {
120 <Self as crate::storage::StorageValue<Value>>::try_get()
121 }
122
123 pub fn translate<O: Decode, F: FnOnce(Option<O>) -> Option<Value>>(
145 f: F,
146 ) -> Result<Option<Value>, ()> {
147 <Self as crate::storage::StorageValue<Value>>::translate(f)
148 }
149
150 pub fn put<Arg: EncodeLike<Value>>(val: Arg) {
152 <Self as crate::storage::StorageValue<Value>>::put(val)
153 }
154
155 pub fn set(val: QueryKind::Query) {
159 <Self as crate::storage::StorageValue<Value>>::set(val)
160 }
161
162 pub fn mutate<R, F: FnOnce(&mut QueryKind::Query) -> R>(f: F) -> R {
164 <Self as crate::storage::StorageValue<Value>>::mutate(f)
165 }
166
167 pub fn mutate_extant<R: Default, F: FnOnce(&mut Value) -> R>(f: F) -> R {
169 <Self as crate::storage::StorageValue<Value>>::mutate_extant(f)
170 }
171
172 pub fn try_mutate<R, E, F: FnOnce(&mut QueryKind::Query) -> Result<R, E>>(
174 f: F,
175 ) -> Result<R, E> {
176 <Self as crate::storage::StorageValue<Value>>::try_mutate(f)
177 }
178
179 pub fn mutate_exists<R, F: FnOnce(&mut Option<Value>) -> R>(f: F) -> R {
181 <Self as crate::storage::StorageValue<Value>>::mutate_exists(f)
182 }
183
184 pub fn try_mutate_exists<R, E, F: FnOnce(&mut Option<Value>) -> Result<R, E>>(
186 f: F,
187 ) -> Result<R, E> {
188 <Self as crate::storage::StorageValue<Value>>::try_mutate_exists(f)
189 }
190
191 pub fn kill() {
193 <Self as crate::storage::StorageValue<Value>>::kill()
194 }
195
196 pub fn take() -> QueryKind::Query {
198 <Self as crate::storage::StorageValue<Value>>::take()
199 }
200
201 pub fn append<Item, EncodeLikeItem>(item: EncodeLikeItem)
211 where
212 Item: Encode,
213 EncodeLikeItem: EncodeLike<Item>,
214 Value: StorageAppend<Item>,
215 {
216 <Self as crate::storage::StorageValue<Value>>::append(item)
217 }
218
219 pub fn decode_len() -> Option<usize>
231 where
232 Value: StorageDecodeLength,
233 {
234 <Self as crate::storage::StorageValue<Value>>::decode_len()
235 }
236
237 pub fn decode_non_dedup_len() -> Option<usize>
253 where
254 Value: StorageDecodeNonDedupLength,
255 {
256 <Self as crate::storage::StorageValue<Value>>::decode_non_dedup_len()
257 }
258
259 pub fn try_append<Item, EncodeLikeItem>(item: EncodeLikeItem) -> Result<(), ()>
263 where
264 Item: Encode,
265 EncodeLikeItem: EncodeLike<Item>,
266 Value: StorageTryAppend<Item>,
267 {
268 <Self as crate::storage::TryAppendValue<Value, Item>>::try_append(item)
269 }
270}
271
272impl<Prefix, Value, QueryKind, OnEmpty> StorageEntryMetadataBuilder
273 for StorageValue<Prefix, Value, QueryKind, OnEmpty>
274where
275 Prefix: StorageInstance,
276 Value: FullCodec + scale_info::StaticTypeInfo,
277 QueryKind: QueryKindTrait<Value, OnEmpty>,
278 OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
279{
280 fn build_metadata(
281 deprecation_status: sp_metadata_ir::ItemDeprecationInfoIR,
282 docs: Vec<&'static str>,
283 entries: &mut Vec<StorageEntryMetadataIR>,
284 ) {
285 let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs };
286
287 let entry = StorageEntryMetadataIR {
288 name: Prefix::STORAGE_PREFIX,
289 modifier: QueryKind::METADATA,
290 ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<Value>()),
291 default: OnEmpty::get().encode(),
292 docs,
293 deprecation_info: deprecation_status,
294 };
295
296 entries.push(entry);
297 }
298}
299
300impl<Prefix, Value, QueryKind, OnEmpty> crate::traits::StorageInfoTrait
301 for StorageValue<Prefix, Value, QueryKind, OnEmpty>
302where
303 Prefix: StorageInstance,
304 Value: FullCodec + MaxEncodedLen,
305 QueryKind: QueryKindTrait<Value, OnEmpty>,
306 OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
307{
308 fn storage_info() -> Vec<StorageInfo> {
309 vec![StorageInfo {
310 pallet_name: Self::pallet_prefix().to_vec(),
311 storage_name: Self::storage_prefix().to_vec(),
312 prefix: Self::hashed_key().to_vec(),
313 max_values: Some(1),
314 max_size: Some(Value::max_encoded_len().saturated_into()),
315 }]
316 }
317}
318
319impl<Prefix, Value, QueryKind, OnEmpty> crate::traits::PartialStorageInfoTrait
321 for StorageValue<Prefix, Value, QueryKind, OnEmpty>
322where
323 Prefix: StorageInstance,
324 Value: FullCodec,
325 QueryKind: QueryKindTrait<Value, OnEmpty>,
326 OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
327{
328 fn partial_storage_info() -> Vec<StorageInfo> {
329 vec![StorageInfo {
330 pallet_name: Self::pallet_prefix().to_vec(),
331 storage_name: Self::storage_prefix().to_vec(),
332 prefix: Self::hashed_key().to_vec(),
333 max_values: Some(1),
334 max_size: None,
335 }]
336 }
337}
338
339#[cfg(test)]
340mod test {
341 use super::*;
342 use crate::storage::types::ValueQuery;
343 use sp_io::{hashing::twox_128, TestExternalities};
344 use sp_metadata_ir::StorageEntryModifierIR;
345
346 struct Prefix;
347 impl StorageInstance for Prefix {
348 fn pallet_prefix() -> &'static str {
349 "test"
350 }
351 const STORAGE_PREFIX: &'static str = "foo";
352 }
353
354 struct ADefault;
355 impl crate::traits::Get<u32> for ADefault {
356 fn get() -> u32 {
357 97
358 }
359 }
360
361 #[test]
362 fn test() {
363 type A = StorageValue<Prefix, u32, OptionQuery>;
364 type AValueQueryWithAnOnEmpty = StorageValue<Prefix, u32, ValueQuery, ADefault>;
365 type B = StorageValue<Prefix, u16, ValueQuery>;
366 type WithLen = StorageValue<Prefix, Vec<u32>>;
367
368 TestExternalities::default().execute_with(|| {
369 assert_eq!(A::hashed_key().to_vec(), [twox_128(b"test"), twox_128(b"foo")].concat());
370 assert_eq!(A::exists(), false);
371 assert_eq!(A::get(), None);
372 assert_eq!(AValueQueryWithAnOnEmpty::get(), 97);
373 assert_eq!(A::try_get(), Err(()));
374
375 A::put(2);
376 assert_eq!(A::exists(), true);
377 assert_eq!(A::get(), Some(2));
378 assert_eq!(AValueQueryWithAnOnEmpty::get(), 2);
379 assert_eq!(A::try_get(), Ok(2));
380 assert_eq!(A::try_get(), Ok(2));
381
382 B::put(4);
383 A::translate::<u16, _>(|v| v.map(Into::into)).unwrap();
384 assert_eq!(A::try_get(), Ok(4));
385
386 A::set(None);
387 assert_eq!(A::try_get(), Err(()));
388
389 A::set(Some(2));
390 assert_eq!(A::try_get(), Ok(2));
391
392 A::mutate(|v| *v = Some(v.unwrap() * 2));
393 assert_eq!(A::try_get(), Ok(4));
394
395 A::set(Some(4));
396 let _: Result<(), ()> = A::try_mutate(|v| {
397 *v = Some(v.unwrap() * 2);
398 Ok(())
399 });
400 assert_eq!(A::try_get(), Ok(8));
401
402 let _: Result<(), ()> = A::try_mutate(|v| {
403 *v = Some(v.unwrap() * 2);
404 Err(())
405 });
406 assert_eq!(A::try_get(), Ok(8));
407
408 A::kill();
409 AValueQueryWithAnOnEmpty::mutate(|v| *v = *v * 2);
410 assert_eq!(AValueQueryWithAnOnEmpty::try_get(), Ok(97 * 2));
411
412 AValueQueryWithAnOnEmpty::kill();
413 let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate(|v| {
414 *v = *v * 2;
415 Ok(())
416 });
417 assert_eq!(AValueQueryWithAnOnEmpty::try_get(), Ok(97 * 2));
418
419 A::kill();
420 assert_eq!(A::try_get(), Err(()));
421
422 let mut entries = vec![];
423 A::build_metadata(
424 sp_metadata_ir::ItemDeprecationInfoIR::NotDeprecated,
425 vec![],
426 &mut entries,
427 );
428 AValueQueryWithAnOnEmpty::build_metadata(
429 sp_metadata_ir::ItemDeprecationInfoIR::NotDeprecated,
430 vec![],
431 &mut entries,
432 );
433 assert_eq!(
434 entries,
435 vec![
436 StorageEntryMetadataIR {
437 name: "foo",
438 modifier: StorageEntryModifierIR::Optional,
439 ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<u32>()),
440 default: Option::<u32>::None.encode(),
441 docs: vec![],
442 deprecation_info: sp_metadata_ir::ItemDeprecationInfoIR::NotDeprecated
443 },
444 StorageEntryMetadataIR {
445 name: "foo",
446 modifier: StorageEntryModifierIR::Default,
447 ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<u32>()),
448 default: 97u32.encode(),
449 docs: vec![],
450 deprecation_info: sp_metadata_ir::ItemDeprecationInfoIR::NotDeprecated
451 }
452 ]
453 );
454
455 WithLen::kill();
456 assert_eq!(WithLen::decode_len(), None);
457 WithLen::append(3);
458 assert_eq!(WithLen::decode_len(), Some(1));
459 });
460 }
461}