odra_ir/module_item/
impl_item.rs

1use std::convert::TryFrom;
2
3use proc_macro2::Ident;
4use syn::{punctuated::Punctuated, token::Comma};
5
6use crate::attrs::partition_attributes;
7
8use super::{constructor::Constructor, method::Method, utils};
9
10/// An item within an implementation block
11///
12/// At this point there is not difference between a [Method] and a default syn::ImplItem
13pub enum ImplItem {
14    /// A `#[odra(init)]` marked function.
15    Constructor(Constructor),
16    /// Unmarked function.
17    Method(Method),
18    /// Any other implementation block item.
19    Other(syn::ImplItem)
20}
21
22impl quote::ToTokens for ImplItem {
23    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
24        match self {
25            Self::Constructor(constructor) => constructor.to_tokens(tokens),
26            Self::Method(method) => method.to_tokens(tokens),
27            Self::Other(other) => other.to_tokens(tokens)
28        }
29    }
30}
31
32impl TryFrom<syn::ImplItem> for ImplItem {
33    type Error = syn::Error;
34
35    fn try_from(value: syn::ImplItem) -> Result<Self, Self::Error> {
36        match value {
37            syn::ImplItem::Method(method) => {
38                let (odra_attrs, _) = partition_attributes(method.attrs.clone())?;
39                if odra_attrs.is_empty() {
40                    return Ok(ImplItem::Method(method.try_into()?));
41                }
42                let is_constructor = odra_attrs.iter().any(|attr| attr.is_constructor());
43                match is_constructor {
44                    true => Ok(ImplItem::Constructor(Constructor::try_from(method)?)),
45                    false => Ok(ImplItem::Method(method.try_into()?))
46                }
47            }
48            other_item => Ok(ImplItem::Other(other_item))
49        }
50    }
51}
52
53pub struct ContractEntrypoint {
54    pub ident: Ident,
55    pub args: Punctuated<syn::PatType, Comma>,
56    pub ret: syn::ReturnType,
57    pub full_sig: syn::Signature
58}
59
60impl From<syn::ImplItemMethod> for ContractEntrypoint {
61    fn from(method: syn::ImplItemMethod) -> Self {
62        let ident = method.sig.ident.to_owned();
63        let args = utils::extract_typed_inputs(&method.sig);
64        let ret = method.clone().sig.output;
65        let full_sig = method.sig;
66        Self {
67            ident,
68            args,
69            ret,
70            full_sig
71        }
72    }
73}
74
75#[cfg(test)]
76mod test {
77    use std::convert::TryFrom;
78
79    use super::ImplItem;
80
81    macro_rules! assert_enum_variant {
82        ($v:expr, $p:pat) => {
83            assert!(if let $p = $v { true } else { false })
84        };
85    }
86
87    #[test]
88    fn test_parse_fn_without_odra_attr() {
89        let item: syn::ImplItem = syn::parse_quote! {
90            #[some(a)]
91            pub fn set_initial_value(&self, value: u32) {
92                self.set_value(value);
93            }
94        };
95        let parsed = ImplItem::try_from(item);
96        assert_enum_variant!(parsed.unwrap(), ImplItem::Method(_));
97    }
98
99    #[test]
100    fn test_parse_fn_without_any_attr() {
101        let item: syn::ImplItem = syn::parse_quote! {
102            pub fn set_initial_value(&self, value: u32) {
103                self.set_value(value);
104            }
105        };
106        let parsed = ImplItem::try_from(item);
107        assert_enum_variant!(parsed.unwrap(), ImplItem::Method(_));
108    }
109
110    #[test]
111    fn test_parse_fn_with_odra_init_attr() {
112        let item: syn::ImplItem = syn::parse_quote! {
113            #[odra(init)]
114            pub fn set_initial_value(&self, value: u32) {
115                self.set_value(value);
116            }
117        };
118        let parsed = ImplItem::try_from(item);
119        assert_enum_variant!(parsed.unwrap(), ImplItem::Constructor(_));
120    }
121
122    #[test]
123    fn test_parse_other_impl_item() {
124        let item: syn::ImplItem = syn::parse_quote! {
125            const A: i32 = 3;
126        };
127        let parsed = ImplItem::try_from(item);
128        assert_enum_variant!(parsed.unwrap(), ImplItem::Other(_));
129    }
130}