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
17pub 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![,]>(); 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 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}