1use proc_macro2::TokenStream;
4use quote::{quote, quote_spanned, ToTokens};
5use std::str::FromStr;
6use syn::{
7 parse_macro_input, parse_quote, spanned::Spanned, Data, DeriveInput, Fields, FieldsNamed,
8 FieldsUnnamed, GenericParam, Generics, Ident, Index, Type, TypeParamBound,
9};
10
11#[proc_macro_attribute]
45pub fn kelk_entry(
46 _attr: proc_macro::TokenStream,
47 mut item: proc_macro::TokenStream,
48) -> proc_macro::TokenStream {
49 let cloned = item.clone();
50 let function = parse_macro_input!(cloned as syn::ItemFn);
51 let name = function.sig.ident.to_string();
52
53 let method = match name.as_ref() {
54 "instantiate" => "create",
55 "process" => "load",
56 "query" => "load",
57 _ => {
58 return proc_macro::TokenStream::from(quote! {
59 compile_error!("entry function should be either \"instantiate\", \"process\", or \"query\""),
60 })
61 }
62 };
63
64 let gen_code = format!(
65 r##"
66 #[cfg(target_arch = "wasm32")]
67 mod __wasm_export_{name} {{
68 #[no_mangle]
69 extern "C" fn {name}(msg_ptr: u64) -> u64 {{
70 let ctx = kelk::context::OwnedContext {{
71 storage: kelk::storage::Storage::{method}(kelk::alloc::boxed::Box::new(kelk::Kelk::new()))
72 .unwrap(),
73 blockchain: kelk::blockchain::Blockchain::new(kelk::alloc::boxed::Box::new(
74 kelk::Kelk::new(),
75 )),
76 }};
77
78 kelk::do_{name}(&super::{name}, ctx.as_ref(), msg_ptr)
79 }}
80 }}
81 "##,
82 );
83
84 let entry = proc_macro::TokenStream::from_str(&gen_code).unwrap();
85 item.extend(entry);
86 item
87}
88
89#[proc_macro_derive(Codec)]
105pub fn derive_codec(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
106 let input = parse_macro_input!(input as DeriveInput);
108
109 let name = input.ident;
111
112 let generics = add_trait_bounds(input.generics, parse_quote!(Codec));
114 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
115
116 let packed_len_body = packed_len_body(&input.data);
118 let (to_bytes_body, from_bytes_body) = codec_body(&input.data);
119
120 let expanded = quote! {
121 impl #impl_generics Codec for #name #ty_generics #where_clause {
122
123 const PACKED_LEN: u32 = #packed_len_body;
124
125 #[inline]
126 fn to_bytes(&self, bytes: &mut [u8]) {
127 debug_assert_eq!(bytes.len(), Self::PACKED_LEN as usize);
128
129 #to_bytes_body
130 }
131
132 #[inline]
133 fn from_bytes(bytes: &[u8]) -> Self {
134 debug_assert_eq!(bytes.len(), Self::PACKED_LEN as usize);
135
136 Self { #from_bytes_body }
137 }
138 }
139 };
140
141 proc_macro::TokenStream::from(expanded)
143}
144
145fn packed_len_body(data: &Data) -> TokenStream {
146 match *data {
147 Data::Struct(ref data) => {
148 match data.fields {
149 Fields::Named(ref fields) => {
150 let recurse = fields.named.iter().map(|f| {
154 let ty = &f.ty;
155 quote_spanned! {f.span()=>
156 <#ty as Codec>::PACKED_LEN
157 }
158 });
159
160 quote! {
161 0 #(+ #recurse)*
162 }
163 }
164 Fields::Unnamed(ref fields) => {
165 let recurse = fields.unnamed.iter().map(|f| {
169 let ty = &f.ty;
170 quote_spanned! {f.span()=>
171 <#ty as Codec>::PACKED_LEN
172 }
173 });
174 quote! {
175 0 #(+ #recurse)*
176 }
177 }
178 Fields::Unit => {
179 quote!(0)
181 }
182 }
183 }
184 Data::Enum(_) | Data::Union(_) => unimplemented!(),
185 }
186}
187
188fn add_trait_bounds(mut generics: Generics, trait_bound: TypeParamBound) -> Generics {
190 for param in &mut generics.params {
191 if let GenericParam::Type(ref mut type_param) = *param {
192 type_param.bounds.push(trait_bound.clone());
193 }
194 }
195 generics
196}
197
198fn codec_body(data: &Data) -> (TokenStream, TokenStream) {
199 match *data {
201 Data::Struct(ref data) => {
202 match data.fields {
203 Fields::Named(FieldsNamed { ref named, .. }) => {
205 let names: Vec<(&Type, &Ident)> = named
209 .iter()
210 .map(|f| (&f.ty, f.ident.as_ref().unwrap()))
211 .collect();
212 codegen_struct(&names)
213 }
214 Fields::Unnamed(FieldsUnnamed { ref unnamed, .. }) => {
216 let mut nums: Vec<(&Type, Index)> = Vec::new();
217 for (i, f) in unnamed.into_iter().enumerate() {
218 nums.push((&f.ty, i.into()));
219 }
220 codegen_struct(&nums)
221 }
222
223 Fields::Unit => {
224 (quote!(0), quote!(0))
226 }
227 }
228 }
229 Data::Enum(_) | Data::Union(_) => unimplemented!(),
230 }
231}
232
233fn codegen_struct<T: ToTokens>(fields: &[(&Type, T)]) -> (TokenStream, TokenStream) {
234 let mut beg_offset = quote! { 0 };
235 let mut recurse_to_bytes = vec![];
236 let mut recurse_from_bytes = vec![];
237
238 for field in fields.iter() {
239 let ty = field.0;
240 let name = &field.1;
241 let struct_size = quote! { <#ty as Codec>::PACKED_LEN as usize};
242 let end_offset = quote! { #beg_offset + #struct_size };
243 let bytes_slice = quote! { bytes[#beg_offset..#end_offset] };
244
245 recurse_to_bytes.push(quote! {
246 Codec::to_bytes(&self.#name, &mut #bytes_slice);
247 });
248
249 recurse_from_bytes.push(quote! {
250 #name: Codec::from_bytes(& #bytes_slice),
251 });
252
253 beg_offset = quote! { #beg_offset + #struct_size };
254 }
255
256 (
257 quote! {
258 #(#recurse_to_bytes)*
259 },
260 quote! {
261 #(#recurse_from_bytes)*
262 },
263 )
264}