odra_ir/
module_item.rs

1use std::convert::TryFrom;
2
3use proc_macro2::TokenStream;
4use quote::ToTokens;
5use syn::{parse::Parse, punctuated::Punctuated, Token};
6
7use self::{module_impl::ModuleImpl, module_struct::ModuleStruct};
8
9pub mod constructor;
10pub mod delegate;
11pub mod impl_item;
12pub mod method;
13pub mod module_impl;
14pub mod module_struct;
15mod utils;
16
17/// Odra module item.
18///
19/// Can be either
20/// - an Odra [`odra_ir::module::ModuleStruct`](`crate::module::ModuleStruct`)
21/// - an Odra [`odra_ir::module::ModuleImpl`](`crate::module::ModuleImpl`)
22///
23/// All the items are based on syn with special variants for Odra `impl` items.
24pub enum ModuleItem {
25    Struct(Box<ModuleStruct>),
26    Impl(Box<ModuleImpl>)
27}
28
29impl ModuleItem {
30    pub fn parse(attr: TokenStream, item: TokenStream) -> Result<Self, syn::Error> {
31        let config = syn::parse2::<ModuleConfiguration>(attr)?;
32
33        let item_struct = syn::parse2::<syn::ItemStruct>(item.clone());
34        let item_impl = syn::parse2::<syn::ItemImpl>(item.clone());
35
36        if let Ok(item) = item_struct {
37            let module_struct = ModuleStruct::try_from(item)?.with_config(config)?;
38            return Ok(ModuleItem::Struct(Box::new(module_struct)));
39        }
40
41        if let Ok(item) = item_impl {
42            let item = ModuleImpl::try_from(item)?;
43            return Ok(ModuleItem::Impl(Box::new(item)));
44        }
45
46        Err(syn::Error::new_spanned(
47            item,
48            "ContractItem is neither a struct nor an impl block."
49        ))
50    }
51}
52
53mod kw {
54    syn::custom_keyword!(events);
55}
56
57#[derive(Debug, Default, Clone)]
58pub struct ModuleConfiguration {
59    pub events: ModuleEvents
60}
61
62impl Parse for ModuleConfiguration {
63    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
64        let mut events = None;
65
66        while !input.is_empty() {
67            if events.is_none() && input.peek(kw::events) {
68                events = Some(input.parse::<ModuleEvents>()?);
69                let _ = input.parse::<Token![,]>(); // optional comma
70                continue;
71            }
72            return Err(input.error("Unexpected token"));
73        }
74
75        Ok(Self {
76            events: events.unwrap_or_default()
77        })
78    }
79}
80
81#[derive(Debug, Default, Clone)]
82pub struct ModuleEvents {
83    pub events: Punctuated<ModuleEvent, Token![,]>,
84    pub submodules_events: Punctuated<ModuleEvent, Token![,]>,
85    pub mappings_events: Punctuated<ModuleEvent, Token![,]>
86}
87
88impl Parse for ModuleEvents {
89    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
90        // a sample input: events = [Event1, Event2, Event3]
91        if input.is_empty() {
92            return Ok(Self::default());
93        }
94        input.parse::<kw::events>()?;
95        input.parse::<Token![=]>()?;
96
97        let content;
98        let _brace_token = syn::bracketed!(content in input);
99        let events = content.parse_terminated::<ModuleEvent, Token![,]>(ModuleEvent::parse)?;
100        Ok(Self {
101            events,
102            submodules_events: Default::default(),
103            mappings_events: Default::default()
104        })
105    }
106}
107
108#[derive(Debug, Clone, PartialEq, Eq, Hash)]
109pub struct ModuleEvent {
110    pub ty: syn::Type
111}
112
113impl Parse for ModuleEvent {
114    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
115        let ty = input.parse::<syn::Type>()?;
116        Ok(ModuleEvent { ty })
117    }
118}
119
120impl ToTokens for ModuleEvent {
121    fn to_tokens(&self, tokens: &mut TokenStream) {
122        self.ty.to_tokens(tokens);
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use quote::quote;
129
130    use super::*;
131
132    #[test]
133    fn invalid_usage() {
134        let result = ModuleItem::parse(
135            quote!(),
136            quote!(
137                fn some_fn(x: u32) -> u32 {
138                    x + 1
139                }
140            )
141        );
142        assert!(result.is_err());
143
144        let result = ModuleItem::parse(
145            quote!(),
146            quote!(
147                enum A {}
148            )
149        );
150        assert!(result.is_err());
151    }
152
153    #[test]
154    fn struct_block() {
155        let result = ModuleItem::parse(
156            quote!(),
157            quote!(
158                struct ContractItem {
159                    x: u32,
160                    name: String
161                }
162            )
163        );
164        assert!(result.is_ok())
165    }
166
167    #[test]
168    fn impl_block() {
169        let result = ModuleItem::parse(
170            quote!(),
171            quote!(
172                impl ContractItem {
173                    fn a() {}
174                }
175            )
176        );
177        assert!(result.is_ok())
178    }
179}