odra_ir/module_item/
module_impl.rs

1use std::convert::TryFrom;
2
3use proc_macro2::Ident;
4use syn::parse_quote;
5
6use crate::module::{Constructor, Method};
7
8use super::{delegate::Delegate, impl_item::ImplItem};
9
10/// Odra module implementation block.
11///
12/// # Examples
13/// ```
14/// # <odra_ir::module::ModuleImpl as TryFrom<syn::ItemImpl>>::try_from(syn::parse_quote! {
15/// impl MyModule {
16///     #[odra(init)]
17///     #[other_attribute]
18///     pub fn set_initial_value(&self, value: u32) {
19///         // initialization logic goes here
20///     }
21///
22///     pub fn set_value(&self, value: u32) {
23///         // logic goes here
24///     }
25/// }
26/// # }).unwrap();
27/// ```
28pub struct ModuleImpl {
29    impl_items: Vec<ImplItem>,
30    ident: Ident,
31    is_trait_implementation: bool
32}
33
34impl ModuleImpl {
35    pub fn impl_items(&self) -> &[ImplItem] {
36        self.impl_items.as_ref()
37    }
38
39    pub fn ident(&self) -> &Ident {
40        &self.ident
41    }
42
43    pub fn custom_impl_items(&self) -> Vec<&ImplItem> {
44        self.impl_items
45            .iter()
46            .filter(|i| matches!(i, ImplItem::Method(_) | ImplItem::Constructor(_)))
47            .collect::<Vec<_>>()
48    }
49
50    pub fn get_public_method_iter(&self) -> impl Iterator<Item = &Method> {
51        self.impl_items
52            .iter()
53            .filter_map(|item| match item {
54                ImplItem::Method(method) => Some(method),
55                _ => None
56            })
57            .filter(|m| self.is_trait_implementation || m.is_public())
58    }
59
60    pub fn get_constructor_iter(&self) -> impl Iterator<Item = &Constructor> {
61        self.impl_items.iter().filter_map(|item| match item {
62            ImplItem::Constructor(c) => Some(c),
63            _ => None
64        })
65    }
66
67    pub fn public_custom_impl_items(&self) -> Vec<&ImplItem> {
68        self.impl_items
69            .iter()
70            .filter(|item| match item {
71                ImplItem::Constructor(_) => true,
72                ImplItem::Method(m) => self.is_trait_implementation || m.is_public(),
73                ImplItem::Other(_) => false
74            })
75            .collect::<Vec<_>>()
76    }
77
78    pub fn is_trait_implementation(&self) -> bool {
79        self.is_trait_implementation
80    }
81}
82
83impl TryFrom<syn::ItemImpl> for ModuleImpl {
84    type Error = syn::Error;
85
86    fn try_from(item_impl: syn::ItemImpl) -> Result<Self, Self::Error> {
87        let is_trait_implementation = item_impl.trait_.is_some();
88        let path = match &*item_impl.self_ty {
89            syn::Type::Path(path) => path,
90            _ => todo!()
91        };
92        let contract_ident = path.path.segments.last().unwrap().clone().ident;
93
94        let delegation_stmts = item_impl
95            .items
96            .clone()
97            .into_iter()
98            .filter_map(|item| match item {
99                syn::ImplItem::Macro(macro_item) => Some(macro_item),
100                _ => None
101            })
102            .map(|macro_item| syn::parse2::<Delegate>(macro_item.mac.tokens))
103            .collect::<Result<Vec<_>, syn::Error>>()?;
104
105        let delegation_stmts = delegation_stmts
106            .into_iter()
107            .flat_map(|d| d.stmts)
108            .collect::<Vec<_>>();
109
110        let delegated_items = delegation_stmts
111            .into_iter()
112            .flat_map(|stmt| {
113                let to = stmt.delegate_to;
114                stmt.delegation_block
115                    .functions
116                    .iter()
117                    .map(|func| {
118                        let attrs = &func.attrs;
119                        let sig = &func.full_sig;
120                        let vis = &func.visibility;
121                        let ident = &func.ident;
122                        let args = &func
123                            .args
124                            .iter()
125                            .map(|ty| ty.pat.clone())
126                            .collect::<Vec<_>>();
127
128                        parse_quote! {
129                            #(#attrs)*
130                            #vis #sig { #to.#ident(#(#args),*) }
131                        }
132                    })
133                    .collect::<Vec<syn::ImplItem>>()
134            })
135            .map(<ImplItem as TryFrom<_>>::try_from)
136            .collect::<Result<Vec<_>, syn::Error>>()?;
137
138        let mut items = item_impl
139            .items
140            .into_iter()
141            .filter(|item| !matches!(item, syn::ImplItem::Macro(_)))
142            .map(<ImplItem as TryFrom<_>>::try_from)
143            .collect::<Result<Vec<_>, syn::Error>>()?;
144
145        items.extend(delegated_items);
146
147        Ok(Self {
148            impl_items: items,
149            ident: contract_ident,
150            is_trait_implementation
151        })
152    }
153}
154
155#[cfg(test)]
156mod test {
157    use super::ModuleImpl;
158
159    #[test]
160    fn impl_items_filtering() {
161        let item_impl: syn::ItemImpl = syn::parse_quote! {
162            impl Contract {
163
164                #[odra(init)]
165                pub fn constructor() {}
166
167                pub(crate) fn crate_public_fn() {}
168
169                pub fn public_fn() {}
170
171                fn private_fn() {}
172
173                delegate! {
174                    to self.a {
175                        pub fn public_fn_del(&self);
176                        pub fn private_fn_del(&self);
177                    }
178                }
179            }
180        };
181        let module_impl = ModuleImpl::try_from(item_impl).unwrap();
182
183        assert_eq!(module_impl.custom_impl_items().len(), 6);
184        assert_eq!(module_impl.public_custom_impl_items().len(), 4);
185    }
186}