casper_contract_macros/
lib.rs

1pub(crate) mod utils;
2
3extern crate proc_macro;
4
5use darling::{ast, FromAttributes, FromMeta};
6use proc_macro::TokenStream;
7use proc_macro2::Span;
8use quote::{format_ident, quote, ToTokens};
9use syn::{
10    parse_macro_input, Fields, ItemEnum, ItemFn, ItemImpl, ItemStruct, ItemTrait, ItemUnion,
11    LitStr, Type,
12};
13
14use casper_executor_wasm_common::flags::EntryPointFlags;
15const CASPER_RESERVED_FALLBACK_EXPORT: &str = "__casper_fallback";
16
17#[derive(Debug, FromAttributes)]
18#[darling(attributes(casper))]
19struct MethodAttribute {
20    #[darling(default)]
21    constructor: bool,
22    #[darling(default)]
23    ignore_state: bool,
24    #[darling(default)]
25    revert_on_error: bool,
26    /// Explicitly mark method as private so it's not externally callable.
27    #[darling(default)]
28    private: bool,
29    #[darling(default)]
30    payable: bool,
31    #[darling(default)]
32    fallback: bool,
33}
34
35#[derive(Debug, FromMeta)]
36struct StructMeta {
37    #[darling(default)]
38    path: Option<syn::Path>,
39    /// Contract state is a special struct that is used to store the state of the contract.
40    #[darling(default)]
41    contract_state: bool,
42    /// Message is a special struct that is used to send messages to other contracts.
43    #[darling(default)]
44    message: bool,
45}
46
47#[derive(Debug, FromMeta)]
48struct EnumMeta {
49    #[darling(default)]
50    path: Option<syn::Path>,
51}
52
53#[derive(Debug, FromMeta)]
54struct TraitMeta {
55    path: Option<syn::Path>,
56    export: Option<bool>,
57}
58
59#[derive(Debug, FromMeta)]
60enum ItemFnMeta {
61    Export,
62}
63
64#[derive(Debug, FromMeta)]
65struct ImplTraitForContractMeta {
66    /// Fully qualified path of the trait.
67    #[darling(default)]
68    path: Option<syn::Path>,
69    /// Does not produce Wasm exports for the entry points.
70    #[darling(default)]
71    compile_as_dependency: bool,
72}
73
74fn generate_call_data_return(output: &syn::ReturnType) -> proc_macro2::TokenStream {
75    match output {
76        syn::ReturnType::Default => {
77            quote! { () }
78        }
79        syn::ReturnType::Type(_, ty) => match ty.as_ref() {
80            Type::Never(_) => {
81                quote! { () }
82            }
83            Type::Reference(reference) => {
84                // ty.uses_lifetimes(options, lifetimes)
85                let mut new_ref = reference.clone();
86                new_ref.lifetime = Some(syn::Lifetime::new("'a", Span::call_site()));
87                quote! { <<#new_ref as core::ops::Deref>::Target as casper_contract_sdk::prelude::borrow::ToOwned>::Owned }
88            }
89            _ => {
90                quote! { #ty }
91            }
92        },
93    }
94}
95
96#[proc_macro_attribute]
97pub fn casper(attrs: TokenStream, item: TokenStream) -> TokenStream {
98    // let attrs: Meta = parse_macro_input!(attrs as Meta);
99    let attr_args = match ast::NestedMeta::parse_meta_list(attrs.into()) {
100        Ok(v) => v,
101        Err(e) => {
102            return TokenStream::from(e.to_compile_error());
103        }
104    };
105
106    let has_fallback_selector = false;
107
108    if let Ok(item_struct) = syn::parse::<ItemStruct>(item.clone()) {
109        let struct_meta = StructMeta::from_list(&attr_args).unwrap();
110        if struct_meta.message {
111            process_casper_message_for_struct(&item_struct, struct_meta)
112        } else if struct_meta.contract_state {
113            // #[casper(contract_state)]
114            process_casper_contract_state_for_struct(&item_struct, struct_meta)
115        } else {
116            // For any other struct that will be part of a schema
117            // #[casper]
118            let partial = generate_casper_state_for_struct(&item_struct, struct_meta);
119            quote! {
120                #partial
121            }
122            .into()
123        }
124    } else if let Ok(item_enum) = syn::parse::<ItemEnum>(item.clone()) {
125        let enum_meta = EnumMeta::from_list(&attr_args).unwrap();
126        let partial = generate_casper_state_for_enum(&item_enum, enum_meta);
127        quote! {
128            #partial
129        }
130        .into()
131    } else if let Ok(item_trait) = syn::parse::<ItemTrait>(item.clone()) {
132        let trait_meta = TraitMeta::from_list(&attr_args).unwrap();
133        casper_trait_definition(item_trait, trait_meta)
134    } else if let Ok(entry_points) = syn::parse::<ItemImpl>(item.clone()) {
135        if let Some((_not, trait_path, _for)) = entry_points.trait_.as_ref() {
136            let impl_meta = ImplTraitForContractMeta::from_list(&attr_args).unwrap();
137            generate_impl_trait_for_contract(&entry_points, trait_path, impl_meta)
138        } else {
139            generate_impl_for_contract(entry_points, has_fallback_selector)
140        }
141    } else if let Ok(func) = syn::parse::<ItemFn>(item.clone()) {
142        let func_meta = ItemFnMeta::from_list(&attr_args).unwrap();
143        match func_meta {
144            ItemFnMeta::Export => generate_export_function(&func),
145        }
146    } else {
147        let err = syn::Error::new(
148            Span::call_site(),
149            "State attribute can only be applied to struct or enum",
150        );
151        TokenStream::from(err.to_compile_error())
152    }
153}
154
155fn process_casper_message_for_struct(
156    item_struct: &ItemStruct,
157    struct_meta: StructMeta,
158) -> TokenStream {
159    let struct_name = &item_struct.ident;
160
161    let crate_path = match &struct_meta.path {
162        Some(path) => quote! { #path },
163        None => quote! { casper_contract_sdk },
164    };
165
166    let borsh_path = {
167        let crate_path_str = match &struct_meta.path {
168            Some(path) => path.to_token_stream().to_string(),
169            None => "casper_contract_sdk".to_string(),
170        };
171        syn::LitStr::new(
172            &format!("{}::serializers::borsh", crate_path_str),
173            Span::call_site(),
174        )
175    };
176
177    let maybe_derive_abi = get_maybe_derive_abi(crate_path.clone());
178
179    let maybe_abi_collectors;
180    let maybe_entrypoint_defs;
181
182    #[cfg(feature = "__abi_generator")]
183    {
184        maybe_abi_collectors = quote! {
185            const _: () = {
186                #[#crate_path::linkme::distributed_slice(#crate_path::abi_generator::ABI_COLLECTORS)]
187                #[linkme(crate = #crate_path::linkme)]
188                static COLLECTOR: fn(&mut #crate_path::abi::Definitions) = |defs| {
189                    defs.populate_one::<#struct_name>();
190                };
191            };
192        };
193
194        maybe_entrypoint_defs = quote! {
195            const _: () = {
196                #[#crate_path::linkme::distributed_slice(#crate_path::abi_generator::MESSAGES)]
197                #[linkme(crate = #crate_path::linkme)]
198                static MESSAGE: #crate_path::abi_generator::Message = #crate_path::abi_generator::Message {
199                    name: <#struct_name as #crate_path::Message>::TOPIC,
200                    decl: concat!(module_path!(), "::", stringify!(#struct_name)),
201                 };
202            };
203        }
204    }
205    #[cfg(not(feature = "__abi_generator"))]
206    {
207        maybe_abi_collectors = quote! {};
208        maybe_entrypoint_defs = quote! {};
209    }
210
211    quote! {
212        #[derive(#crate_path::serializers::borsh::BorshSerialize)]
213        #[borsh(crate = #borsh_path)]
214        #maybe_derive_abi
215        #item_struct
216
217        impl #crate_path::Message for #struct_name {
218            const TOPIC: &'static str = stringify!(#struct_name);
219
220            #[inline]
221            fn payload(&self) -> Vec<u8> {
222                #crate_path::serializers::borsh::to_vec(self).unwrap()
223            }
224        }
225
226        #maybe_abi_collectors
227        #maybe_entrypoint_defs
228
229    }
230    .into()
231}
232
233fn generate_export_function(func: &ItemFn) -> TokenStream {
234    let func_name = &func.sig.ident;
235    let mut arg_names = Vec::new();
236    let mut args_attrs = Vec::new();
237    for input in &func.sig.inputs {
238        let (name, ty) = match input {
239            syn::FnArg::Receiver(receiver) => {
240                todo!("{receiver:?}")
241            }
242            syn::FnArg::Typed(typed) => match typed.pat.as_ref() {
243                syn::Pat::Ident(ident) => (&ident.ident, &typed.ty),
244                _ => todo!("export: other typed variant"),
245            },
246        };
247        arg_names.push(name);
248        args_attrs.push(quote! {
249            #name: #ty
250        });
251    }
252    let _ctor_name = format_ident!("{func_name}_ctor");
253
254    let exported_func_name = format_ident!("__casper_export_{func_name}");
255    quote! {
256        #[export_name = stringify!(#func_name)]
257        #[no_mangle]
258        pub extern "C" fn #exported_func_name() {
259            #[cfg(target_arch = "wasm32")]
260            {
261                casper_contract_sdk::set_panic_hook();
262            }
263
264            #func
265
266            #[derive(casper_contract_sdk::serializers::borsh::BorshDeserialize)]
267            #[borsh(crate = "casper_contract_sdk::serializers::borsh")]
268            struct Arguments {
269                #(#args_attrs,)*
270            }
271            let input = casper_contract_sdk::prelude::casper::copy_input();
272            let args: Arguments = casper_contract_sdk::serializers::borsh::from_slice(&input).unwrap();
273            let _ret = #func_name(#(args.#arg_names,)*);
274        }
275
276        #[cfg(not(target_arch = "wasm32"))]
277        #func
278
279        #[cfg(not(target_arch = "wasm32"))]
280        const _: () = {
281            #[casper_contract_sdk::linkme::distributed_slice(casper_contract_sdk::casper::native::ENTRY_POINTS)]
282            #[linkme(crate = casper_contract_sdk::linkme)]
283            pub static EXPORTS: casper_contract_sdk::casper::native::EntryPoint = casper_contract_sdk::casper::native::EntryPoint {
284                kind: casper_contract_sdk::casper::native::EntryPointKind::Function { name: stringify!(#func_name) },
285                fptr: || { #exported_func_name(); },
286                module_path: module_path!(),
287                file: file!(),
288                line: line!(),
289            };
290        };
291    }.into()
292}
293
294fn generate_impl_for_contract(
295    mut entry_points: ItemImpl,
296    _has_fallback_selector: bool,
297) -> TokenStream {
298    #[cfg(feature = "__abi_generator")]
299    let mut populate_definitions_linkme = Vec::new();
300    let impl_trait = match entry_points.trait_.as_ref() {
301        Some((None, path, _for)) => Some(path),
302        Some((Some(_not), _path, _for)) => {
303            panic!("Exclamation mark not supported");
304        }
305        None => None,
306    };
307    let struct_name = match entry_points.self_ty.as_ref() {
308        Type::Path(ref path) => &path.path,
309
310        other => todo!("Unsupported {other:?}"),
311    };
312    let defs = vec![quote! {}]; // TODO: Dummy element which may not be necessary but is used for expansion later
313    #[cfg(feature = "__abi_generator")]
314    let mut defs = defs;
315    #[cfg(feature = "__abi_generator")]
316    let mut defs_linkme = Vec::new();
317    let mut names = Vec::new();
318    let mut extern_entry_points = Vec::new();
319    let _abi_generator_entry_points = [quote! {}]; // TODO: Dummy element which may not be necessary but is used for expansion later
320    let mut manifest_entry_point_enum_variants = Vec::new();
321    let mut manifest_entry_point_enum_match_name = Vec::new();
322    let mut manifest_entry_point_input_data = Vec::new();
323    let mut extra_code = Vec::new();
324
325    for entry_point in &mut entry_points.items {
326        let mut populate_definitions = Vec::new();
327
328        let method_attribute;
329        let mut flag_value = EntryPointFlags::empty();
330
331        // let selector_value;
332
333        let func = match entry_point {
334            syn::ImplItem::Const(_) => todo!("Const"),
335            syn::ImplItem::Fn(ref mut func) => {
336                let vis = &func.vis;
337                match vis {
338                    syn::Visibility::Public(_) => {}
339                    syn::Visibility::Inherited => {
340                        // As the doc says this "usually means private"
341                        continue;
342                    }
343                    syn::Visibility::Restricted(_restricted) => {}
344                }
345
346                // func.sig.re
347                let never_returns = match &func.sig.output {
348                    syn::ReturnType::Default => false,
349                    syn::ReturnType::Type(_, ty) => matches!(ty.as_ref(), Type::Never(_)),
350                };
351
352                method_attribute = MethodAttribute::from_attributes(&func.attrs).unwrap();
353
354                func.attrs.clear();
355
356                let func_name = func.sig.ident.clone();
357                if func_name.to_string().starts_with("__casper_") {
358                    return TokenStream::from(
359                        syn::Error::new(
360                            Span::call_site(),
361                            "Function names starting with '__casper_' are reserved",
362                        )
363                        .to_compile_error(),
364                    );
365                }
366
367                let export_name = if method_attribute.fallback {
368                    format_ident!("{}", CASPER_RESERVED_FALLBACK_EXPORT)
369                } else {
370                    format_ident!("{}", &func_name)
371                };
372
373                names.push(func_name.clone());
374
375                let arg_names_and_types = func
376                    .sig
377                    .inputs
378                    .iter()
379                    .filter_map(|arg| match arg {
380                        syn::FnArg::Receiver(_) => None,
381                        syn::FnArg::Typed(typed) => match typed.pat.as_ref() {
382                            syn::Pat::Ident(ident) => Some((&ident.ident, &typed.ty)),
383                            _ => todo!(),
384                        },
385                    })
386                    .collect::<Vec<_>>();
387
388                let arg_names: Vec<_> =
389                    arg_names_and_types.iter().map(|(name, _ty)| name).collect();
390                let arg_types: Vec<_> = arg_names_and_types.iter().map(|(_name, ty)| ty).collect();
391                let arg_attrs: Vec<_> = arg_names_and_types
392                    .iter()
393                    .map(|(name, ty)| quote! { #name: #ty })
394                    .collect();
395
396                // Entry point has &self or &mut self
397                let mut entry_point_requires_state: bool = false;
398
399                let handle_write_state = match func.sig.inputs.first() {
400                    Some(syn::FnArg::Receiver(receiver)) if receiver.mutability.is_some() => {
401                        entry_point_requires_state = true;
402
403                        if !never_returns && receiver.reference.is_some() {
404                            // &mut self does write updated state
405                            Some(quote! {
406                                casper_contract_sdk::casper::write_state(&instance).unwrap();
407                            })
408                        } else {
409                            // mut self does not write updated state as the
410                            // method call
411                            // will consume self and there's nothing to persist.
412                            None
413                        }
414                    }
415                    Some(syn::FnArg::Receiver(receiver)) if receiver.mutability.is_none() => {
416                        entry_point_requires_state = true;
417
418                        // &self does not write state
419                        None
420                    }
421                    Some(syn::FnArg::Receiver(receiver)) if receiver.lifetime().is_some() => {
422                        panic!("Lifetimes are currently not supported");
423                    }
424                    Some(_) | None => {
425                        if !never_returns && method_attribute.constructor {
426                            Some(quote! {
427                                casper_contract_sdk::casper::write_state(&_ret).unwrap();
428                            })
429                        } else {
430                            None
431                        }
432                    }
433                };
434
435                let call_data_return_lifetime = if method_attribute.constructor {
436                    quote! {
437                        #struct_name
438                    }
439                } else {
440                    generate_call_data_return(&func.sig.output)
441                };
442                let _func_sig_output = match &func.sig.output {
443                    syn::ReturnType::Default => {
444                        quote! { () }
445                    }
446                    syn::ReturnType::Type(_, ty) => {
447                        quote! { #ty }
448                    }
449                };
450
451                let handle_ret = if never_returns {
452                    None
453                } else {
454                    match func.sig.output {
455                        syn::ReturnType::Default => {
456                            // Do not call casper_return if there is no return value
457                            None
458                        }
459                        _ if method_attribute.constructor => {
460                            // Constructor does not return serialized state but is expected to save
461                            // state, or explicitly revert.
462                            // TODO: Add support for Result<Self, Error> and revert_on_error if
463                            // possible.
464                            Some(quote! {
465                                let _ = flags; // hide the warning
466                            })
467                        }
468                        syn::ReturnType::Type(..) => {
469                            // There is a return value so call casper_return.
470                            Some(quote! {
471                                let ret_bytes = casper_contract_sdk::serializers::borsh::to_vec(&_ret).unwrap();
472                                casper_contract_sdk::casper::ret(flags, Some(&ret_bytes));
473                            })
474                        }
475                    }
476                };
477
478                assert_eq!(arg_names.len(), arg_types.len());
479
480                let mut prelude = Vec::new();
481
482                prelude.push(quote! {
483                    #[derive(casper_contract_sdk::serializers::borsh::BorshDeserialize)]
484                    #[borsh(crate = "casper_contract_sdk::serializers::borsh")]
485                    struct Arguments {
486                        #(#arg_attrs,)*
487                    }
488
489
490                    let input = casper_contract_sdk::prelude::casper::copy_input();
491                    let args: Arguments = casper_contract_sdk::serializers::borsh::from_slice(&input).unwrap();
492                });
493
494                if method_attribute.constructor {
495                    prelude.push(quote! {
496                        if casper_contract_sdk::casper::has_state().unwrap() {
497                            panic!("State of the contract is already present; unable to proceed with the constructor");
498                        }
499                    });
500                }
501
502                if !method_attribute.payable {
503                    let panic_msg = format!(
504                        r#"Entry point "{func_name}" is not payable and does not accept tokens"#
505                    );
506                    prelude.push(quote! {
507                        if casper_contract_sdk::casper::transferred_value() != 0 {
508                            // TODO: Be precise and unambigious about the error
509                            panic!(#panic_msg);
510                        }
511                    });
512                }
513
514                let handle_err = if !never_returns && method_attribute.revert_on_error {
515                    if let syn::ReturnType::Default = func.sig.output {
516                        panic!("Cannot revert on error if there is no return value");
517                    }
518
519                    quote! {
520                        let _ret: &Result<_, _> = &_ret;
521                        if _ret.is_err() {
522                            flags |= casper_contract_sdk::casper_executor_wasm_common::flags::ReturnFlags::REVERT;
523                        }
524
525                    }
526                } else {
527                    quote! {}
528                };
529
530                let handle_call = if entry_point_requires_state {
531                    quote! {
532                        let mut instance: #struct_name = casper_contract_sdk::casper::read_state().unwrap();
533                        let _ret = instance.#func_name(#(args.#arg_names,)*);
534                    }
535                } else if method_attribute.constructor {
536                    quote! {
537                        let _ret = <#struct_name>::#func_name(#(args.#arg_names,)*);
538                    }
539                } else {
540                    quote! {
541                        let _ret = <#struct_name>::#func_name(#(args.#arg_names,)*);
542                    }
543                };
544                if method_attribute.constructor {
545                    flag_value |= EntryPointFlags::CONSTRUCTOR;
546                }
547
548                if method_attribute.fallback {
549                    flag_value |= EntryPointFlags::FALLBACK;
550                }
551
552                let _bits = flag_value.bits();
553
554                let extern_func_name = format_ident!("__casper_export_{func_name}");
555
556                extern_entry_points.push(quote! {
557
558                    #[export_name = stringify!(#export_name)]
559                    #vis extern "C" fn #extern_func_name() {
560                        // Set panic hook (assumes std is enabled etc.)
561                        #[cfg(target_arch = "wasm32")]
562                        {
563                            casper_contract_sdk::set_panic_hook();
564                        }
565
566                        #(#prelude;)*
567
568                        let mut flags = casper_contract_sdk::casper_executor_wasm_common::flags::ReturnFlags::empty();
569
570                        #handle_call;
571
572                        #handle_err;
573
574                        #handle_write_state;
575
576                        #handle_ret;
577                    }
578
579                    #[cfg(not(target_arch = "wasm32"))]
580                    const _: () = {
581                        #[casper_contract_sdk::linkme::distributed_slice(casper_contract_sdk::casper::native::ENTRY_POINTS)]
582                        #[linkme(crate = casper_contract_sdk::linkme)]
583                        pub static EXPORTS: casper_contract_sdk::casper::native::EntryPoint = casper_contract_sdk::casper::native::EntryPoint {
584                            kind: casper_contract_sdk::casper::native::EntryPointKind::SmartContract { name: stringify!(#export_name), struct_name: stringify!(#struct_name) },
585                            fptr: || -> () { #extern_func_name(); },
586                            module_path: module_path!(),
587                            file: file!(),
588                            line: line!(),
589                        };
590                    };
591
592                });
593
594                manifest_entry_point_enum_variants.push(quote! {
595                    #func_name {
596                        #(#arg_names: #arg_types,)*
597                    }
598                });
599
600                manifest_entry_point_enum_match_name.push(quote! {
601                    #func_name
602                });
603
604                manifest_entry_point_input_data.push(quote! {
605                    Self::#func_name { #(#arg_names,)* } => {
606                        let into_tuple = (#(#arg_names,)*);
607                        into_tuple.serialize(writer)
608                    }
609                });
610
611                match entry_points.self_ty.as_ref() {
612                    Type::Path(ref path) => {
613                        let ident = syn::Ident::new(
614                            &format!("{}_{}", path.path.get_ident().unwrap(), func_name),
615                            Span::call_site(),
616                        );
617
618                        let input_data_content = if arg_names.is_empty() {
619                            quote! {
620                                None
621                            }
622                        } else {
623                            quote! {
624                                Some(casper_contract_sdk::serializers::borsh::to_vec(&self).expect("Serialization to succeed"))
625                            }
626                        };
627
628                        let self_ty =
629                            if method_attribute.constructor || method_attribute.ignore_state {
630                                None
631                            } else {
632                                Some(quote! {
633                                   &self,
634                                })
635                            };
636
637                        if !method_attribute.fallback {
638                            extra_code.push(quote! {
639                                        pub fn #func_name<'a>(#self_ty #(#arg_names: #arg_types,)*) -> impl casper_contract_sdk::ToCallData<Return<'a> = #call_data_return_lifetime> {
640                                            #[derive(casper_contract_sdk::serializers::borsh::BorshSerialize, PartialEq, Debug)]
641                                            #[borsh(crate = "casper_contract_sdk::serializers::borsh")]
642                                            struct #ident {
643                                                #(#arg_names: #arg_types,)*
644                                            }
645
646                                            impl casper_contract_sdk::ToCallData for #ident {
647                                                // const SELECTOR: vm_common::selector::Selector = vm_common::selector::Selector::new(#selector_value);
648
649                                                type Return<'a> = #call_data_return_lifetime;
650
651                                                fn entry_point(&self) -> &str { stringify!(#func_name) }
652
653                                                fn input_data(&self) -> Option<casper_contract_sdk::serializers::borsh::__private::maybestd::vec::Vec<u8>> {
654                                                    #input_data_content
655                                                }
656                                            }
657
658                                            #ident {
659                                                #(#arg_names,)*
660                                            }
661                                        }
662                                    });
663                        }
664                    }
665
666                    _ => todo!("Different self_ty currently unsupported"),
667                }
668
669                func.clone()
670            }
671            syn::ImplItem::Type(_) => todo!(),
672            syn::ImplItem::Macro(_) => todo!(),
673            syn::ImplItem::Verbatim(_) => todo!(),
674            _ => todo!(),
675        };
676
677        let mut args = Vec::new();
678
679        for input in &func.sig.inputs {
680            let typed = match input {
681                syn::FnArg::Receiver(_receiver) => continue,
682                syn::FnArg::Typed(typed) => typed,
683            };
684            // typed
685            let name = match &typed.pat.as_ref() {
686                syn::Pat::Const(_) => todo!("Const"),
687                syn::Pat::Ident(ident) => ident,
688                syn::Pat::Lit(_) => todo!("Lit"),
689                syn::Pat::Macro(_) => todo!("Macro"),
690                syn::Pat::Or(_) => todo!("Or"),
691                syn::Pat::Paren(_) => todo!("Paren"),
692                syn::Pat::Path(_) => todo!("Path"),
693                syn::Pat::Range(_) => todo!("Range"),
694                syn::Pat::Reference(_) => todo!("Reference"),
695                syn::Pat::Rest(_) => todo!("Rest"),
696                syn::Pat::Slice(_) => todo!("Slice"),
697                syn::Pat::Struct(_) => todo!("Struct"),
698                syn::Pat::Tuple(_) => todo!("Tuple"),
699                syn::Pat::TupleStruct(_) => todo!("TupleStruct"),
700                syn::Pat::Type(_) => todo!("Type"),
701                syn::Pat::Verbatim(_) => todo!("Verbatim"),
702                syn::Pat::Wild(_) => todo!("Wild"),
703                _ => todo!(),
704            };
705            let ty = &typed.ty;
706
707            populate_definitions.push(quote! {
708                definitions.populate_one::<#ty>();
709            });
710
711            args.push(quote! {
712                casper_contract_sdk::schema::SchemaArgument {
713                    name: stringify!(#name).into(),
714                    decl: <#ty as casper_contract_sdk::abi::CasperABI>::declaration(),
715                }
716            });
717        }
718
719        #[cfg(feature = "__abi_generator")]
720        {
721            let bits = flag_value.bits();
722
723            let result = match &func.sig.output {
724                syn::ReturnType::Default => {
725                    populate_definitions.push(quote! {
726                        definitions.populate_one::<()>();
727                    });
728
729                    quote! { <() as casper_contract_sdk::abi::CasperABI>::declaration() }
730                }
731                syn::ReturnType::Type(_, ty) => match ty.as_ref() {
732                    Type::Never(_) => {
733                        populate_definitions.push(quote! {
734                            definitions.populate_one::<()>();
735                        });
736
737                        quote! { <() as casper_contract_sdk::abi::CasperABI>::declaration() }
738                    }
739                    _ => {
740                        populate_definitions.push(quote! {
741                            definitions.populate_one::<#ty>();
742                        });
743
744                        quote! { <#ty as casper_contract_sdk::abi::CasperABI>::declaration() }
745                    }
746                },
747            };
748
749            let func_name = &func.sig.ident;
750
751            let linkme_schema_entry_point_ident =
752                format_ident!("__casper_schema_entry_point_{func_name}");
753
754            defs.push(quote! {
755                fn #linkme_schema_entry_point_ident() -> casper_contract_sdk::schema::SchemaEntryPoint {
756                    casper_contract_sdk::schema::SchemaEntryPoint {
757                        name: stringify!(#func_name).into(),
758                        arguments: vec![ #(#args,)* ],
759                        result: #result,
760                        flags: casper_contract_sdk::casper_executor_wasm_common::flags::EntryPointFlags::from_bits(#bits).unwrap(),
761                    }
762                }
763            });
764            defs_linkme.push(linkme_schema_entry_point_ident);
765
766            let linkme_abi_populate_defs_ident =
767                format_ident!("__casper_populate_definitions_{func_name}");
768
769            defs.push(quote! {
770                fn #linkme_abi_populate_defs_ident(definitions: &mut casper_contract_sdk::abi::Definitions) {
771                    #(#populate_definitions)*;
772                }
773            });
774
775            populate_definitions_linkme.push(linkme_abi_populate_defs_ident);
776        }
777    }
778    // let entry_points_len = entry_points.len();
779    let st_name = struct_name.get_ident().unwrap();
780    let maybe_abi_collectors;
781    let maybe_entrypoint_defs;
782    #[cfg(feature = "__abi_generator")]
783    {
784        maybe_abi_collectors = quote! {
785            #(
786                const _: () = {
787                    #[casper_contract_sdk::linkme::distributed_slice(casper_contract_sdk::abi_generator::ABI_COLLECTORS)]
788                    #[linkme(crate = casper_contract_sdk::linkme)]
789                    static COLLECTOR: fn(&mut casper_contract_sdk::abi::Definitions) = <#struct_name>::#populate_definitions_linkme;
790                };
791            )*
792        };
793
794        maybe_entrypoint_defs = quote! {
795            #(
796
797                const _: () = {
798                    #[casper_contract_sdk::linkme::distributed_slice(casper_contract_sdk::abi_generator::ENTRYPOINTS)]
799                    #[linkme(crate = casper_contract_sdk::linkme)]
800                    static ENTRY_POINTS: fn() -> casper_contract_sdk::schema::SchemaEntryPoint = <#struct_name>::#defs_linkme;
801                };
802            )*
803        }
804    }
805    #[cfg(not(feature = "__abi_generator"))]
806    {
807        maybe_abi_collectors = quote! {};
808        maybe_entrypoint_defs = quote! {};
809    }
810    let handle_manifest = match impl_trait {
811        Some(_path) => {
812            // Do not generate a manifest if we're implementing a trait.
813            // The expectation is that you list the traits below under
814            // #[derive(Contract)] and the rest is handled by a macro
815            None
816        }
817        None => Some(quote! {
818
819            #[doc(hidden)]
820            impl #struct_name {
821                #(#defs)*
822            }
823
824            #maybe_abi_collectors
825
826            #maybe_entrypoint_defs
827            #(#extern_entry_points)*
828
829        }),
830    };
831    let ref_struct_name = format_ident!("{st_name}Ref");
832
833    quote! {
834        #entry_points
835
836        #handle_manifest
837
838        impl #ref_struct_name {
839            #(#extra_code)*
840        }
841    }
842    .into()
843}
844
845fn generate_impl_trait_for_contract(
846    entry_points: &ItemImpl,
847    trait_path: &syn::Path,
848    impl_meta: ImplTraitForContractMeta,
849) -> TokenStream {
850    let self_ty = match entry_points.self_ty.as_ref() {
851        Type::Path(ref path) => &path.path,
852        other => todo!("Unsupported {other:?}"),
853    };
854    let self_ty = quote! { #self_ty };
855    let mut code = Vec::new();
856
857    let trait_name = trait_path
858        .segments
859        .last()
860        .expect("Expected non-empty path")
861        .ident
862        .clone();
863
864    let path_to_macro = match &impl_meta.path {
865        Some(path) => quote! { #path },
866        None => {
867            quote! { self }
868        }
869    };
870
871    let path_to_crate: proc_macro2::TokenStream = match &impl_meta.path {
872        Some(path) => {
873            let crate_name = path
874                .segments
875                .first()
876                .expect("Expected non-empty path")
877                .ident
878                .clone();
879
880            if crate_name == "crate" {
881                // This is local, can't refer by absolute path
882                quote! { #path }
883            } else {
884                quote! { #crate_name }
885            }
886        }
887        None => {
888            quote! { self }
889        }
890    };
891
892    let macro_name = format_ident!("enumerate_{trait_name}_symbols");
893    let ref_trait = format_ident!("{}Ext", trait_path.segments.last().unwrap().ident);
894    let ref_name = format_ident!("{}Ref", self_ty.to_token_stream().to_string());
895
896    let visitor = if impl_meta.compile_as_dependency {
897        quote! {
898            const _: () = {
899                macro_rules! visitor {
900                    ($( $vis:vis $name:ident as $export_name:ident => $dispatch:ident , $schema:ident , )*) => {
901                        $(
902                            $vis fn $name() {
903                                #path_to_macro::$dispatch::<#self_ty>();
904                            }
905                        )*
906                    }
907                }
908
909                #path_to_crate::#macro_name!(visitor);
910            };
911        }
912    } else {
913        quote! {
914            const _: () = {
915                macro_rules! visitor {
916                    ($( $vis:vis $name:ident as $export_name:ident => $dispatch:ident , $schema:ident , )*) => {
917                        $(
918                            #[export_name = stringify!($export_name)]
919                            $vis extern "C" fn $name() {
920                                #path_to_macro::$dispatch::<#self_ty>();
921                            }
922
923                            #[cfg(not(target_arch = "wasm32"))]
924                            const _: () = {
925                                #[casper_contract_sdk::linkme::distributed_slice(casper_contract_sdk::casper::native::ENTRY_POINTS)]
926                                #[linkme(crate = casper_contract_sdk::linkme)]
927                                pub static EXPORTS: casper_contract_sdk::casper::native::EntryPoint = casper_contract_sdk::casper::native::EntryPoint {
928                                    kind: casper_contract_sdk::casper::native::EntryPointKind::TraitImpl { trait_name: stringify!(#trait_name), impl_name: stringify!(#self_ty), name: stringify!($export_name) },
929                                    fptr: || -> () { $name(); },
930                                    module_path: module_path!(),
931                                    file: file!(),
932                                    line: line!(),
933                                };
934                            };
935
936                            const _: () = {
937                                #[casper_contract_sdk::linkme::distributed_slice(casper_contract_sdk::abi_generator::ENTRYPOINTS)]
938                                #[linkme(crate = casper_contract_sdk::linkme)]
939                                static ENTRY_POINTS: fn() -> casper_contract_sdk::schema::SchemaEntryPoint = <#ref_name as #ref_trait>::$schema;
940                            };
941                        )*
942                    }
943                }
944
945                #path_to_crate::#macro_name!(visitor);
946            };
947        }
948    };
949
950    code.push(visitor);
951
952    let ref_trait = format_ident!("{}Ext", trait_path.require_ident().unwrap());
953
954    let ref_name = format_ident!("{self_ty}Ref");
955
956    code.push(quote! {
957        impl #ref_trait for #ref_name {}
958    });
959
960    quote! {
961        #entry_points
962
963        #(#code)*
964    }
965    .into()
966}
967
968fn casper_trait_definition(mut item_trait: ItemTrait, trait_meta: TraitMeta) -> TokenStream {
969    let crate_path = match &trait_meta.path {
970        Some(path) => quote! { #path },
971        None => quote! { casper_contract_sdk },
972    };
973
974    let borsh_path = {
975        let crate_path_str = match &trait_meta.path {
976            Some(path) => path.to_token_stream().to_string(),
977            None => "casper_contract_sdk".to_string(),
978        };
979        syn::LitStr::new(
980            &format!("{}::serializers::borsh", crate_path_str),
981            Span::call_site(),
982        )
983    };
984
985    let trait_name = &item_trait.ident;
986
987    let vis = &item_trait.vis;
988    let mut dispatch_functions = Vec::new();
989    // let mut dispatch_table = Vec::new();
990    let mut extra_code = Vec::new();
991    // let mut schema_entry_points = Vec::new();
992    let mut populate_definitions = Vec::new();
993    let mut macro_symbols = Vec::new();
994    for entry_point in &mut item_trait.items {
995        match entry_point {
996            syn::TraitItem::Const(_) => todo!("Const"),
997            syn::TraitItem::Fn(func) => {
998                // let vis  =func.vis;
999                let method_attribute = MethodAttribute::from_attributes(&func.attrs).unwrap();
1000                func.attrs.clear();
1001
1002                if method_attribute.private {
1003                    continue;
1004                }
1005
1006                let func_name = func.sig.ident.clone();
1007                let func_name_str = func_name.to_string();
1008
1009                if func_name.to_string().starts_with("__casper_") {
1010                    return TokenStream::from(
1011                        syn::Error::new(
1012                            Span::call_site(),
1013                            "Function names starting with '__casper_' are reserved",
1014                        )
1015                        .to_compile_error(),
1016                    );
1017                }
1018
1019                let export_name = if method_attribute.fallback {
1020                    CASPER_RESERVED_FALLBACK_EXPORT.to_string()
1021                } else {
1022                    format!("{}_{}", trait_name, func_name_str)
1023                };
1024
1025                let export_ident = format_ident!("{}", &func_name_str);
1026
1027                let result = match &func.sig.output {
1028                    syn::ReturnType::Default => {
1029                        populate_definitions.push(quote! {
1030                            definitions.populate_one::<()>();
1031                        });
1032
1033                        quote! { <() as #crate_path::abi::CasperABI>::declaration() }
1034                    }
1035                    syn::ReturnType::Type(_, ty) => {
1036                        if let Type::Never(_) = ty.as_ref() {
1037                            populate_definitions.push(quote! {
1038                                definitions.populate_one::<()>();
1039                            });
1040
1041                            quote! { <() as #crate_path::abi::CasperABI>::declaration() }
1042                        } else {
1043                            populate_definitions.push(quote! {
1044                                definitions.populate_one::<#ty>();
1045                            });
1046
1047                            quote! { <#ty as #crate_path::abi::CasperABI>::declaration() }
1048                        }
1049                    }
1050                };
1051
1052                let call_data_return_lifetime = generate_call_data_return(&func.sig.output);
1053
1054                let dispatch_func_name = format_ident!("{trait_name}_{func_name}_dispatch");
1055
1056                let arg_names_and_types = func
1057                    .sig
1058                    .inputs
1059                    .iter()
1060                    .filter_map(|arg| match arg {
1061                        syn::FnArg::Receiver(_) => None,
1062                        syn::FnArg::Typed(typed) => match typed.pat.as_ref() {
1063                            syn::Pat::Ident(ident) => Some((&ident.ident, &typed.ty)),
1064                            _ => todo!(),
1065                        },
1066                    })
1067                    .collect::<Vec<_>>();
1068
1069                let arg_names: Vec<_> =
1070                    arg_names_and_types.iter().map(|(name, _ty)| name).collect();
1071                let arg_types: Vec<_> = arg_names_and_types.iter().map(|(_name, ty)| ty).collect();
1072                // let mut arg_pairs: Vec
1073                let args_attrs: Vec<_> = arg_names_and_types
1074                    .iter()
1075                    .map(|(name, ty)| {
1076                        quote! {
1077                            #name: #ty
1078                        }
1079                    })
1080                    .collect();
1081
1082                let mut args = Vec::new();
1083                for (name, ty) in &arg_names_and_types {
1084                    populate_definitions.push(quote! {
1085                        definitions.populate_one::<()>();
1086                    });
1087                    args.push(quote! {
1088                        casper_contract_sdk::schema::SchemaArgument {
1089                            name: stringify!(#name).into(),
1090                            decl: <#ty as #crate_path::abi::CasperABI>::declaration(),
1091                        }
1092                    });
1093                }
1094
1095                let flags = EntryPointFlags::empty();
1096
1097                let _flags = flags.bits();
1098
1099                let handle_dispatch = match func.sig.inputs.first() {
1100                    Some(syn::FnArg::Receiver(_receiver)) => {
1101                        assert!(
1102                            !method_attribute.private,
1103                            "can't make dispatcher for private method"
1104                        );
1105                        quote! {
1106                            #vis extern "C" fn #dispatch_func_name<T>()
1107                            where
1108                                T: #trait_name
1109                                    + #crate_path::serializers::borsh::BorshDeserialize
1110                                    + #crate_path::serializers::borsh::BorshSerialize
1111                                    + Default
1112                            {
1113                                #[derive(#crate_path::serializers::borsh::BorshDeserialize)]
1114                                #[borsh(crate = #borsh_path)]
1115                                struct Arguments {
1116                                    #(#args_attrs,)*
1117                                }
1118
1119                                let mut flags = #crate_path::casper_executor_wasm_common::flags::ReturnFlags::empty();
1120                                let mut instance: T = #crate_path::casper::read_state().unwrap();
1121                                let input = #crate_path::prelude::casper::copy_input();
1122                                let args: Arguments = #crate_path::serializers::borsh::from_slice(&input).unwrap();
1123
1124                                let ret = instance.#func_name(#(args.#arg_names,)*);
1125
1126                                #crate_path::casper::write_state(&instance).unwrap();
1127
1128                                let ret_bytes = #crate_path::serializers::borsh::to_vec(&ret).unwrap();
1129                                #crate_path::casper::ret(flags, Some(&ret_bytes));
1130                            }
1131                        }
1132                    }
1133
1134                    None | Some(syn::FnArg::Typed(_)) => {
1135                        assert!(
1136                            !method_attribute.private,
1137                            "can't make dispatcher for private static method"
1138                        );
1139                        quote! {
1140                            #vis extern "C"  fn #dispatch_func_name<T: #trait_name>() {
1141                                #[derive(#crate_path::serializers::borsh::BorshDeserialize)]
1142                                #[borsh(crate = #borsh_path)]
1143                                struct Arguments {
1144                                    #(#args_attrs,)*
1145                                }
1146
1147
1148                                let input = #crate_path::prelude::casper::copy_input();
1149                                let args: Arguments = #crate_path::serializers::borsh::from_slice(&input).unwrap();
1150
1151
1152                                let _ret = <T as #trait_name>::#func_name(#(args.#arg_names,)*);
1153                            }
1154                        }
1155                    }
1156                };
1157
1158                let schema_helper_ident = format_ident!("__casper_schema_entry_point_{func_name}");
1159                extra_code.push(quote! {
1160                    fn #schema_helper_ident () -> casper_contract_sdk::schema::SchemaEntryPoint {
1161                        casper_contract_sdk::schema::SchemaEntryPoint {
1162                            name: stringify!(#export_name).into(),
1163                            arguments: vec![ #(#args,)* ],
1164                            result: #result,
1165                            flags: casper_contract_sdk::casper_executor_wasm_common::flags::EntryPointFlags::from_bits(#_flags).unwrap(),
1166                        }
1167                    }
1168                });
1169
1170                macro_symbols.push(quote! {
1171                    #vis #func_name as #export_ident => #dispatch_func_name , #schema_helper_ident
1172                });
1173
1174                dispatch_functions.push(quote! { #handle_dispatch });
1175
1176                let input_data_content = if arg_names.is_empty() {
1177                    quote! {
1178                        None
1179                    }
1180                } else {
1181                    quote! {
1182                        Some(#crate_path::serializers::borsh::to_vec(&self).expect("Serialization to succeed"))
1183                    }
1184                };
1185                let self_ty = if method_attribute.constructor || method_attribute.ignore_state {
1186                    None
1187                } else {
1188                    Some(quote! {
1189                        self,
1190                    })
1191                };
1192
1193                let is_fallback = method_attribute.fallback;
1194
1195                if !is_fallback {
1196                    let entry_point_lit = LitStr::new(&export_name, Span::call_site());
1197                    extra_code.push(quote! {
1198                        fn #func_name<'a>(#self_ty #(#arg_names: #arg_types,)*) -> impl #crate_path::ToCallData<Return<'a> = #call_data_return_lifetime> {
1199                            #[derive(#crate_path::serializers::borsh::BorshSerialize)]
1200                            #[borsh(crate = #borsh_path)]
1201                            struct CallData {
1202                                #(pub #arg_names: #arg_types,)*
1203                            }
1204
1205                            impl #crate_path::ToCallData for CallData {
1206                                // const SELECTOR: vm_common::selector::Selector = vm_common::selector::Selector::new(#selector_value);
1207
1208                                type Return<'a> = #call_data_return_lifetime;
1209
1210                                fn entry_point(&self) -> &str { #entry_point_lit }
1211                                fn input_data(&self) -> Option<Vec<u8>> {
1212                                    #input_data_content
1213                                }
1214                            }
1215
1216                            CallData {
1217                                #(#arg_names,)*
1218                            }
1219                        }
1220                    });
1221                }
1222            }
1223            syn::TraitItem::Type(_) => {
1224                return syn::Error::new(Span::call_site(), "Unsupported generic associated types")
1225                    .to_compile_error()
1226                    .into();
1227            }
1228            syn::TraitItem::Macro(_) => todo!("Macro"),
1229            syn::TraitItem::Verbatim(_) => todo!("Verbatim"),
1230            other => todo!("Other {other:?}"),
1231        }
1232    }
1233    let ref_struct = format_ident!("{trait_name}Ref");
1234    let ref_struct_trait = format_ident!("{trait_name}Ext");
1235
1236    let macro_name = format_ident!("enumerate_{trait_name}_symbols");
1237
1238    let maybe_exported_macro = if !trait_meta.export.unwrap_or(false) {
1239        quote! {
1240            #[allow(non_snake_case, unused_macros)]
1241            macro_rules! #macro_name {
1242                ($mac:ident) => {
1243                    $mac! {
1244                        #(#macro_symbols,)*
1245                    }
1246                }
1247            }
1248            pub(crate) use #macro_name;
1249        }
1250    } else {
1251        quote! {
1252            #[allow(non_snake_case, unused_macros)]
1253            #[macro_export]
1254            macro_rules! #macro_name {
1255                ($mac:ident) => {
1256                    $mac! {
1257                        #(#macro_symbols,)*
1258                    }
1259                }
1260            }
1261        }
1262    };
1263
1264    let extension_struct = quote! {
1265        #vis trait #ref_struct_trait: Sized {
1266            #(#extra_code)*
1267        }
1268
1269        #vis struct #ref_struct;
1270
1271        impl #ref_struct {
1272
1273        }
1274
1275        #maybe_exported_macro
1276
1277        #(#dispatch_functions)*
1278
1279        // TODO: Rename Ext with Ref, since Ref struct can be pub(crate)'d
1280        impl #ref_struct_trait for #ref_struct {}
1281            impl #crate_path::ContractRef for #ref_struct {
1282                fn new() -> Self {
1283                    #ref_struct
1284                }
1285            }
1286    };
1287    quote! {
1288        #item_trait
1289
1290        #extension_struct
1291    }
1292    .into()
1293}
1294
1295fn generate_casper_state_for_struct(
1296    item_struct: &ItemStruct,
1297    struct_meta: StructMeta,
1298) -> impl quote::ToTokens {
1299    let crate_path = match &struct_meta.path {
1300        Some(path) => quote! { #path },
1301        None => quote! { casper_contract_sdk },
1302    };
1303
1304    let borsh_path = {
1305        let crate_path_str = match &struct_meta.path {
1306            Some(path) => path.to_token_stream().to_string(),
1307            None => "casper_contract_sdk".to_string(),
1308        };
1309        syn::LitStr::new(
1310            &format!("{}::serializers::borsh", crate_path_str),
1311            Span::call_site(),
1312        )
1313    };
1314    let maybe_derive_abi = get_maybe_derive_abi(crate_path.clone());
1315
1316    quote! {
1317        #[derive(#crate_path::serializers::borsh::BorshSerialize, #crate_path::serializers::borsh::BorshDeserialize)]
1318        #[borsh(crate = #borsh_path)]
1319        #maybe_derive_abi
1320        #item_struct
1321    }
1322}
1323
1324fn generate_casper_state_for_enum(
1325    item_enum: &ItemEnum,
1326    enum_meta: EnumMeta,
1327) -> impl quote::ToTokens {
1328    let crate_path = match &enum_meta.path {
1329        Some(path) => quote! { #path },
1330        None => quote! { casper_contract_sdk },
1331    };
1332
1333    let borsh_path = {
1334        let crate_path_str = match &enum_meta.path {
1335            Some(path) => path.to_token_stream().to_string(),
1336            None => "casper_contract_sdk".to_string(),
1337        };
1338        syn::LitStr::new(
1339            &format!("{}::serializers::borsh", crate_path_str),
1340            Span::call_site(),
1341        )
1342    };
1343
1344    let maybe_derive_abi = get_maybe_derive_abi(crate_path.clone());
1345
1346    quote! {
1347        #[derive(#crate_path::serializers::borsh::BorshSerialize, #crate_path::serializers::borsh::BorshDeserialize)]
1348        #[borsh(use_discriminant = true, crate = #borsh_path)]
1349        #[repr(u32)]
1350        #maybe_derive_abi
1351        #item_enum
1352    }
1353}
1354
1355fn get_maybe_derive_abi(_crate_path: impl ToTokens) -> impl ToTokens {
1356    #[cfg(feature = "__abi_generator")]
1357    {
1358        quote! {
1359            #[derive(#_crate_path::macros::CasperABI)]
1360        }
1361    }
1362
1363    #[cfg(not(feature = "__abi_generator"))]
1364    {
1365        quote! {}
1366    }
1367}
1368
1369fn process_casper_contract_state_for_struct(
1370    contract_struct: &ItemStruct,
1371    struct_meta: StructMeta,
1372) -> TokenStream {
1373    let struct_name = &contract_struct.ident;
1374    let ref_name = format_ident!("{struct_name}Ref");
1375    let vis = &contract_struct.vis;
1376
1377    let crate_path = match &struct_meta.path {
1378        Some(path) => quote! { #path },
1379        None => quote! { casper_contract_sdk },
1380    };
1381    let borsh_path = {
1382        let crate_path_str = match &struct_meta.path {
1383            Some(path) => path.to_token_stream().to_string(),
1384            None => "casper_contract_sdk".to_string(),
1385        };
1386        syn::LitStr::new(
1387            &format!("{}::serializers::borsh", crate_path_str),
1388            Span::call_site(),
1389        )
1390    };
1391
1392    let maybe_derive_abi = get_maybe_derive_abi(crate_path.clone());
1393
1394    // Optionally, generate a schema export if the appropriate flag
1395    // is set.
1396    let maybe_casper_schema = {
1397        #[cfg(feature = "__embed_schema")]
1398        quote! {
1399            const SCHEMA: Option<&str> = option_env!("__CARGO_CASPER_INJECT_SCHEMA_MARKER");
1400
1401            #[no_mangle]
1402            pub extern "C" fn __casper_schema() {
1403                use #crate_path::casper::ret;
1404                use #crate_path::casper_executor_wasm_common::flags::ReturnFlags;
1405                let bytes = SCHEMA.unwrap_or_default().as_bytes();
1406                ret(ReturnFlags::empty(), Some(bytes));
1407            }
1408        }
1409        #[cfg(not(feature = "__embed_schema"))]
1410        quote! {}
1411    };
1412
1413    quote! {
1414        #[derive(#crate_path::serializers::borsh::BorshSerialize, #crate_path::serializers::borsh::BorshDeserialize)]
1415        #[borsh(crate = #borsh_path)]
1416        #maybe_derive_abi
1417        #contract_struct
1418
1419        #vis struct #ref_name;
1420
1421        impl #crate_path::ContractRef for #ref_name {
1422            fn new() -> Self {
1423                #ref_name
1424            }
1425        }
1426
1427        #maybe_casper_schema
1428    }
1429    .into()
1430}
1431
1432#[proc_macro_attribute]
1433pub fn entry_point(_attr: TokenStream, item: TokenStream) -> TokenStream {
1434    let func = parse_macro_input!(item as ItemFn);
1435
1436    let vis = &func.vis;
1437    let _sig = &func.sig;
1438    let func_name = &func.sig.ident;
1439
1440    let block = &func.block;
1441
1442    let mut handle_args = Vec::new();
1443    let mut params = Vec::new();
1444
1445    for arg in &func.sig.inputs {
1446        let typed = match arg {
1447            syn::FnArg::Receiver(_) => todo!(),
1448            syn::FnArg::Typed(typed) => typed,
1449        };
1450
1451        let name = match typed.pat.as_ref() {
1452            syn::Pat::Ident(ident) => &ident.ident,
1453            _ => todo!(),
1454        };
1455
1456        let ty = &typed.ty;
1457
1458        let tok = quote! {
1459            let #typed = casper_contract_sdk::get_named_arg(stringify!(#name)).expect("should get named arg");
1460        };
1461        handle_args.push(tok);
1462
1463        let tok2 = quote! {
1464            (stringify!(#name), <#ty>::cl_type())
1465        };
1466        params.push(tok2);
1467    }
1468
1469    // let len = params.len();
1470
1471    let output = &func.sig.output;
1472
1473    // let const_tok =
1474
1475    let gen = quote! {
1476        // const paste!(#func_name, _ENTRY_POINT): &str = #func_name;
1477
1478        #vis fn #func_name() {
1479            #(#handle_args)*;
1480
1481            let closure = || #output {
1482                #block
1483            };
1484
1485            let result = closure();
1486
1487            // casper_contract_sdk::EntryPoint {
1488            //     name: #func_name,
1489            //     params: &[
1490            //         #(#params,)*
1491            //     ],
1492            //     func: closure,
1493            // }
1494
1495            result.expect("should work")
1496        }
1497    };
1498
1499    println!("{gen}");
1500
1501    // quote!(fn foo() {})
1502    // item
1503    gen.into()
1504}
1505
1506// #[proc_macro_derive(CasperSchema, attributes(casper))]
1507// pub fn derive_casper_schema(input: TokenStream) -> TokenStream {
1508//     let contract = parse_macro_input!(input as DeriveInput);
1509
1510//     let contract_attributes = ContractAttributes::from_attributes(&contract.attrs).unwrap();
1511
1512//     let _data_struct = match &contract.data {
1513//         Data::Struct(s) => s,
1514//         Data::Enum(_) => todo!("Enum"),
1515//         Data::Union(_) => todo!("Union"),
1516//     };
1517
1518//     let name = &contract.ident;
1519
1520//     // let mut extra_code = Vec::new();
1521//     // if let Some(traits) = contract_attributes.impl_traits {
1522//     //     for path in traits.iter() {
1523//     //         let ext_struct = format_ident!("{}Ref", path.require_ident().unwrap());
1524//     //         extra_code.push(quote! {
1525//     //             {
1526//     //                 let entry_points = <#ext_struct>::__casper_schema_entry_points();
1527//     //                 schema.entry_points.extend(entry_points);
1528//     //                 <#ext_struct>::__casper_populate_definitions(&mut schema.definitions);
1529//     //             }
1530//     //         });
1531//     //     }
1532
1533//     //     let macro_name = format_ident!("enumerate_{path}_symbols");
1534
1535//     //     extra_code.push(quote! {
1536//     //         const _: () = {
1537//     //             macro_rules! #macro_name {
1538//     //                 ($mac:ident) => {
1539//     //                     $mac! {
1540//     //                         #(#extra_code)*
1541//     //                     }
1542//     //                 }
1543//     //             }
1544//     //         }
1545//     //     })
1546//     // }
1547
1548//     quote! {
1549//         impl casper_contract_sdk::schema::CasperSchema for #name {
1550//             fn schema() -> casper_contract_sdk::schema::Schema {
1551//                 let mut schema = Self::__casper_schema();
1552
1553//                 // #(#extra_code)*;
1554
1555//                 schema
1556//                 // schema.entry_points.ext
1557//             }
1558//         }
1559//     }
1560//     .into()
1561// }
1562
1563#[proc_macro_derive(CasperABI, attributes(casper))]
1564pub fn derive_casper_abi(input: TokenStream) -> TokenStream {
1565    let res = if let Ok(input) = syn::parse::<ItemStruct>(input.clone()) {
1566        let mut populate_definitions = Vec::new();
1567        let name = input.ident.clone();
1568        let mut items = Vec::new();
1569        for field in &input.fields {
1570            match &field.ty {
1571                Type::Path(path) => {
1572                    for segment in &path.path.segments {
1573                        let field_name = &field.ident;
1574
1575                        populate_definitions.push(quote! {
1576                            definitions.populate_one::<#segment>();
1577                        });
1578
1579                        items.push(quote! {
1580                            casper_contract_sdk::abi::StructField {
1581                                name: stringify!(#field_name).into(),
1582                                decl: <#segment>::declaration(),
1583                            }
1584                        });
1585                    }
1586                }
1587                other_ty => todo!("Unsupported type {other_ty:?}"),
1588            }
1589        }
1590
1591        Ok(quote! {
1592            impl casper_contract_sdk::abi::CasperABI for #name {
1593                fn populate_definitions(definitions: &mut casper_contract_sdk::abi::Definitions) {
1594                    #(#populate_definitions)*;
1595                }
1596
1597                fn declaration() -> casper_contract_sdk::abi::Declaration {
1598                    const DECL: &str = concat!(module_path!(), "::", stringify!(#name));
1599                    DECL.into()
1600                }
1601
1602                fn definition() -> casper_contract_sdk::abi::Definition {
1603                    casper_contract_sdk::abi::Definition::Struct {
1604                        items: vec![
1605                            #(#items,)*
1606                        ]
1607                    }
1608                }
1609            }
1610        })
1611    } else if let Ok(input) = syn::parse::<ItemEnum>(input.clone()) {
1612        // TODO: Check visibility
1613        let name = input.ident.clone();
1614
1615        let mut all_definitions = Vec::new();
1616        let mut all_variants = Vec::new();
1617        let mut populate_definitions = Vec::new();
1618        let mut has_unit_definition = false;
1619
1620        // populate_definitions.push(quote! {
1621        //     definitions.populate_one::<#name>();
1622        // });
1623
1624        all_definitions.push(quote! {
1625            casper_contract_sdk::abi::Definition::Enum {
1626                name: stringify!(#name).into(),
1627            }
1628        });
1629
1630        let mut current_discriminant = 0;
1631
1632        for variant in &input.variants {
1633            if let Some(discriminant) = &variant.discriminant {
1634                match &discriminant.1 {
1635                    syn::Expr::Lit(lit) => match &lit.lit {
1636                        syn::Lit::Int(int) => {
1637                            current_discriminant = int.base10_parse::<u64>().unwrap();
1638                        }
1639                        _ => todo!(),
1640                    },
1641                    _ => todo!(),
1642                }
1643            }
1644
1645            let variant_name = &variant.ident;
1646
1647            let variant_decl = match &variant.fields {
1648                Fields::Unit => {
1649                    // NOTE: Generate an empty struct here for a definition.
1650                    if !has_unit_definition {
1651                        populate_definitions.push(quote! {
1652                            definitions.populate_one::<()>();
1653                        });
1654                        has_unit_definition = true;
1655                    }
1656
1657                    quote! {
1658                        <()>::declaration()
1659                    }
1660                }
1661                Fields::Named(named) => {
1662                    let mut fields = Vec::new();
1663
1664                    let variant_name = format_ident!("{name}_{variant_name}");
1665
1666                    for field in &named.named {
1667                        let field_name = &field.ident;
1668                        match &field.ty {
1669                            Type::Path(path) => {
1670                                populate_definitions.push(quote! {
1671                                    definitions.populate_one::<#path>();
1672                                });
1673
1674                                fields.push(quote! {
1675                                    casper_contract_sdk::abi::StructField {
1676                                        name: stringify!(#field_name).into(),
1677                                        decl: <#path as casper_contract_sdk::abi::CasperABI>::declaration()
1678                                    }
1679                                });
1680                            }
1681                            other_ty => todo!("Unsupported type {other_ty:?}"),
1682                        }
1683                    }
1684
1685                    populate_definitions.push(quote! {
1686                        definitions.populate_custom(
1687                            stringify!(#variant_name).into(),
1688                            casper_contract_sdk::abi::Definition::Struct {
1689                                items: vec![
1690                                    #(#fields,)*
1691                                ],
1692                            });
1693                    });
1694
1695                    quote! {
1696                        stringify!(#variant_name).into()
1697                    }
1698                }
1699                Fields::Unnamed(unnamed_fields) => {
1700                    let mut fields = Vec::new();
1701
1702                    let variant_name = format_ident!("{name}_{variant_name}");
1703
1704                    for field in &unnamed_fields.unnamed {
1705                        match &field.ty {
1706                            Type::Path(path) => {
1707                                for segment in &path.path.segments {
1708                                    let type_name = &segment.ident;
1709                                    populate_definitions.push(quote! {
1710                                        definitions.populate_one::<#type_name>();
1711                                    });
1712
1713                                    fields.push(quote! {
1714                                        <#type_name as casper_contract_sdk::abi::CasperABI>::declaration()
1715                                    });
1716                                }
1717                            }
1718                            other_ty => todo!("Unsupported type {other_ty:?}"),
1719                        }
1720                    }
1721
1722                    populate_definitions.push(quote! {
1723                        definitions.populate_custom(
1724                            stringify!(#variant_name).into(),
1725                            casper_contract_sdk::abi::Definition::Tuple {
1726                                items: vec![
1727                                    #(#fields,)*
1728                                ],
1729                            });
1730                    });
1731
1732                    quote! {
1733                        stringify!(#variant_name).into()
1734                    }
1735                }
1736            };
1737
1738            all_variants.push(quote! {
1739                casper_contract_sdk::abi::EnumVariant {
1740                    name: stringify!(#variant_name).into(),
1741                    discriminant: #current_discriminant,
1742                    decl: #variant_decl,
1743                }
1744            });
1745
1746            current_discriminant += 1;
1747        }
1748
1749        Ok(quote! {
1750            impl casper_contract_sdk::abi::CasperABI for #name {
1751                fn populate_definitions(definitions: &mut casper_contract_sdk::abi::Definitions) {
1752                    #(#populate_definitions)*;
1753                }
1754
1755                fn declaration() -> casper_contract_sdk::abi::Declaration {
1756                    const DECL: &str = concat!(module_path!(), "::", stringify!(#name));
1757                    DECL.into()
1758                }
1759
1760                fn definition() -> casper_contract_sdk::abi::Definition {
1761                    casper_contract_sdk::abi::Definition::Enum {
1762                        items: vec![
1763                            #(#all_variants,)*
1764                        ],
1765                    }
1766                }
1767            }
1768        })
1769    } else if syn::parse::<ItemUnion>(input).is_ok() {
1770        Err(syn::Error::new(
1771            Span::call_site(),
1772            "Borsh schema does not support unions yet.",
1773        ))
1774    } else {
1775        // Derive macros can only be defined on structs, enums, and unions.
1776        unreachable!()
1777    };
1778    TokenStream::from(match res {
1779        Ok(res) => res,
1780        Err(err) => err.to_compile_error(),
1781    })
1782}
1783
1784#[proc_macro]
1785pub fn blake2b256(input: TokenStream) -> TokenStream {
1786    let input = parse_macro_input!(input as LitStr);
1787    let bytes = input.value();
1788
1789    let hash = utils::compute_blake2b256(bytes.as_bytes());
1790
1791    TokenStream::from(quote! {
1792        [ #(#hash),* ]
1793    })
1794}
1795
1796#[proc_macro]
1797pub fn test(item: TokenStream) -> TokenStream {
1798    let input = parse_macro_input!(item as ItemFn);
1799    TokenStream::from(quote! {
1800        #[test]
1801        #input
1802    })
1803}
1804
1805/// `PanicOnDefault` generates implementation for `Default` trait that panics with the following
1806/// message `The contract is not initialized` when `default()` is called.
1807///
1808/// This is to protect againsts default-initialization of contracts in a situation where no
1809/// constructor is called, and an entrypoint is invoked before the contract is initialized.
1810#[proc_macro_derive(PanicOnDefault)]
1811pub fn derive_no_default(item: TokenStream) -> TokenStream {
1812    if let Ok(input) = syn::parse::<ItemStruct>(item) {
1813        let name = &input.ident;
1814        TokenStream::from(quote! {
1815            impl ::core::default::Default for #name {
1816                fn default() -> Self {
1817                    panic!("The contract is not initialized");
1818                }
1819            }
1820        })
1821    } else {
1822        TokenStream::from(
1823            syn::Error::new(
1824                Span::call_site(),
1825                "PanicOnDefault can only be used on type declarations sections.",
1826            )
1827            .to_compile_error(),
1828        )
1829    }
1830}