odra_ir/module_item/
constructor.rs

1use std::convert::TryFrom;
2
3use crate::attrs::{partition_attributes, OdraAttribute};
4use quote::{quote, ToTokens};
5
6use super::utils;
7
8/// Odra constructor definition.
9///
10/// # Examples
11/// ```
12/// # <odra_ir::module::Constructor as TryFrom<syn::ImplItemMethod>>::try_from(syn::parse_quote! {
13/// #[odra(init)]
14/// #[other_attribute]
15/// pub fn set_initial_value(&self, value: u32) {
16///     // initialization logic goes here
17/// }
18/// # }).unwrap();
19/// ```
20pub struct Constructor {
21    pub attrs: Vec<OdraAttribute>,
22    pub impl_item: syn::ImplItemMethod,
23    pub ident: syn::Ident,
24    pub args: syn::punctuated::Punctuated<syn::PatType, syn::token::Comma>,
25    pub full_sig: syn::Signature
26}
27
28impl ToTokens for Constructor {
29    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
30        let is_non_reentrant = self.attrs.iter().any(OdraAttribute::is_non_reentrant);
31        let is_mut = utils::is_mut(&self.full_sig);
32        let name = &self.ident.to_string();
33        let args = &self
34            .args
35            .iter()
36            .flat_map(|arg| {
37                let name = &*arg.pat;
38                let (ty, is_slice) = utils::ty(arg);
39                let is_ref = utils::is_ref(arg);
40                let ty = quote!(<#ty as odra::types::CLTyped>::cl_type());
41                quote! {
42                    odra::types::contract_def::Argument {
43                        ident: odra::prelude::string::String::from(stringify!(#name)),
44                        ty: #ty,
45                        is_ref: #is_ref,
46                        is_slice: #is_slice
47                    },
48                }
49            })
50            .collect::<proc_macro2::TokenStream>();
51        let ep = quote! {
52            odra::types::contract_def::Entrypoint {
53                ident: odra::prelude::string::String::from(#name),
54                args: odra::prelude::vec![#args],
55                is_mut: #is_mut,
56                ret: odra::types::CLType::Unit,
57                ty: odra::types::contract_def::EntrypointType::Constructor { non_reentrant: #is_non_reentrant },
58            },
59        };
60
61        tokens.extend(ep)
62    }
63}
64
65impl TryFrom<syn::ImplItemMethod> for Constructor {
66    type Error = syn::Error;
67
68    fn try_from(method: syn::ImplItemMethod) -> Result<Self, Self::Error> {
69        let (odra_attrs, attrs) = partition_attributes(method.clone().attrs).unwrap();
70        let ident = method.sig.ident.to_owned();
71        let args = utils::extract_typed_inputs(&method.sig);
72        if let syn::ReturnType::Type(_, _) = method.sig.output {
73            return Err(syn::Error::new_spanned(
74                method.sig,
75                "Constructor must not return value."
76            ));
77        }
78        let full_sig = method.sig.clone();
79
80        Ok(Self {
81            attrs: odra_attrs,
82            impl_item: syn::ImplItemMethod { attrs, ..method },
83            ident,
84            args,
85            full_sig
86        })
87    }
88}
89
90#[cfg(test)]
91mod test {
92    use std::convert::TryFrom;
93
94    use super::Constructor;
95
96    #[test]
97    fn test_attrs() {
98        let item: syn::ImplItemMethod = syn::parse_quote! {
99            #[odra(init, non_reentrant)]
100            #[some(a)]
101            pub fn set_initial_value(&self, value: u32) {
102                self.set_value(value);
103            }
104        };
105        let constructor = Constructor::try_from(item).unwrap();
106        assert_eq!(constructor.attrs.len(), 1);
107    }
108}