ink_macro/storage/
storage_layout.rs1use proc_macro2::TokenStream as TokenStream2;
16use quote::quote;
17use syn::spanned::Spanned;
18
19fn field_layout<'a>(
20 variant: &'a synstructure::VariantInfo,
21) -> impl Iterator<Item = TokenStream2> + 'a {
22 variant.ast().fields.iter().enumerate().map(|(i, field)| {
23 let ident = match field.ident.as_ref() {
24 Some(ident) => {
25 let ident_str = ident.to_string();
26 quote! { #ident_str }
27 }
28 None => {
29 let index = i.to_string();
30 quote! { #index }
31 }
32 };
33 let ty = &field.ty;
34 quote! {
35 ::ink::metadata::layout::FieldLayout::new(
36 #ident,
37 <#ty as ::ink::storage::traits::StorageLayout>::layout(__key),
38 )
39 }
40 })
41}
42
43fn storage_layout_struct(s: &synstructure::Structure) -> TokenStream2 {
44 assert!(
45 matches!(s.ast().data, syn::Data::Struct(_)),
46 "s must be a struct item"
47 );
48 assert!(
49 s.variants().len() == 1,
50 "structs must have at most one variant"
51 );
52 let struct_ident = s.ast().ident.clone();
53 let variant: &synstructure::VariantInfo = &s.variants()[0];
54 let field_layouts = field_layout(variant);
55 s.gen_impl(quote! {
56 gen impl ::ink::storage::traits::StorageLayout for @Self {
57 fn layout(__key: &::ink::primitives::Key) -> ::ink::metadata::layout::Layout {
58 ::ink::metadata::layout::Layout::Struct(
59 ::ink::metadata::layout::StructLayout::new(
60 ::core::stringify!(#struct_ident),
61 [
62 #(#field_layouts ,)*
63 ]
64 )
65 )
66 }
67 }
68 })
69}
70
71fn storage_layout_enum(s: &synstructure::Structure) -> TokenStream2 {
72 assert!(
73 matches!(s.ast().data, syn::Data::Enum(_)),
74 "s must be an enum item"
75 );
76
77 if s.variants().len() > 256 {
78 return syn::Error::new(
79 s.ast().span(),
80 "Currently only enums with at most 256 variants are supported.",
81 )
82 .to_compile_error()
83 }
84
85 let variant_layouts = s.variants().iter().enumerate().map(|(n, variant)| {
86 let variant_ident = variant.ast().ident;
87 let discriminant = variant
88 .ast()
89 .discriminant
90 .as_ref()
91 .map(|(_, expr)| quote! { #expr })
92 .unwrap_or_else(|| quote! { #n });
93 let field_layouts = field_layout(variant);
94 quote! {
95 {
96 (
97 ::ink::metadata::layout::Discriminant::from(#discriminant),
98 ::ink::metadata::layout::StructLayout::new(
99 ::core::stringify!(#variant_ident),
100 [
101 #(#field_layouts ,)*
102 ]
103 ),
104 )
105 }
106 }
107 });
108 let enum_ident = s.ast().ident.clone();
109 s.gen_impl(quote! {
110 gen impl ::ink::storage::traits::StorageLayout for @Self {
111 fn layout(__key: &::ink::primitives::Key) -> ::ink::metadata::layout::Layout {
112 ::ink::metadata::layout::Layout::Enum(
113 ::ink::metadata::layout::EnumLayout::new(
114 ::core::stringify!(#enum_ident),
115 ::ink::metadata::layout::LayoutKey::from(__key),
116 [
117 #(#variant_layouts ,)*
118 ]
119 )
120 )
121 }
122 }
123 })
124}
125
126pub fn storage_layout_derive(mut s: synstructure::Structure) -> TokenStream2 {
127 s.bind_with(|_| synstructure::BindStyle::Move)
128 .add_bounds(synstructure::AddBounds::Fields)
129 .underscore_const(true);
130 match &s.ast().data {
131 syn::Data::Struct(_) => storage_layout_struct(&s),
132 syn::Data::Enum(_) => storage_layout_enum(&s),
133 _ => panic!("cannot derive `StorageLayout` for Rust `union` items"),
134 }
135}