fvm_macros/lib.rs
1//! Provides the macros needed to write fvm contracts
2//!
3//! ## Basic Usage
4//! Macros can be used in contract for simplify contract preparation process,
5//! like this:
6//! ``` no_run
7//! # #[cfg(not(feature = "advance"))]
8//! use fvm_macros::contract;
9//! # #[cfg(not(feature = "advance"))]
10//! use fvm_macros::storage;
11//! # #[cfg(not(feature = "advance"))]
12//! use fvm_std::collections::hyper_map::HyperMap;
13//!
14//! # #[cfg(not(feature = "advance"))]
15//! #[storage]
16//! pub struct SetHash {
17//! map: HyperMap<String, String>,
18//! }
19//!
20//! # #[cfg(not(feature = "advance"))]
21//! #[contract]
22//! impl SetHash {
23//! fn new() -> Self {
24//! Self { map: HyperMap::new() }
25//! }
26//!
27//! pub fn set_hash(&mut self, key: String, value: String) {
28//! self.map.insert(key, value);
29//! }
30//!
31//! pub fn get_hash(&mut self, key: String) -> &String {
32//! self.map.get(&key).unwrap()
33//! }
34//! }
35//! ```
36//!
37//! ## Contract Mode
38//! Now write contract have two mode:
39//! - **normal mode**
40//!
41//! Contract with normal mode limited data storage format, and user can write contract more
42//! convenient. User can use all macros except `advance_contract`.
43//! - **advance mode**
44//!
45//! Contract with advance mode not limited data storage format, and it must open advance
46//! feature with `fvm-std` and 'fvm-macros' lib to use this mode. The execute speed of contract
47//! in this mode would be fast then the contract in normal mode.
48//!
49
50use proc_macro::{TokenStream};
51use quote::{
52 quote,
53};
54
55use syn::{parse_macro_input};
56use syn::ItemImpl;
57
58#[cfg(any(not(feature = "advance"), doc))]
59mod storage;
60#[cfg(any(not(feature = "advance"), doc))]
61mod contract;
62mod common;
63mod cross;
64#[cfg(feature = "advance")]
65mod advance;
66
67/// Generate storage attribute for struct
68///
69/// ## Example
70/// ``` no_run
71/// # #[cfg(not(feature = "advance"))]
72/// use fvm_macros::storage;
73/// # #[cfg(not(feature = "advance"))]
74/// use fvm_std::collections::hyper_map::HyperMap;
75///
76/// # #[cfg(not(feature = "advance"))]
77/// #[storage]
78/// pub struct SetHash {
79/// map: HyperMap<String, String>,
80/// }
81/// ```
82///
83/// The attributes of the marked struct by `#[storage]` will be automatically
84/// mapped to the ledger
85///
86#[cfg(any(not(feature = "advance"), doc))]
87#[proc_macro_attribute]
88pub fn storage(attr: TokenStream, item: TokenStream) -> TokenStream {
89 //实现SpreadLayout接口
90 storage::generate_storage(attr.into(), item.into()).into()
91}
92
93
94/// Mark method implementation as contract method
95///
96/// ## Example
97/// ``` no_run
98/// # #[cfg(not(feature = "advance"))]
99/// use fvm_macros::contract;
100/// # #[cfg(not(feature = "advance"))]
101/// use fvm_macros::storage;
102/// # #[cfg(not(feature = "advance"))]
103/// use fvm_std::collections::hyper_map::HyperMap;
104///
105/// # #[cfg(not(feature = "advance"))]
106/// #[storage]
107/// pub struct SetHash {
108/// map: HyperMap<String, String>,
109/// }
110///
111/// # #[cfg(not(feature = "advance"))]
112/// #[contract]
113/// impl SetHash {
114/// fn new() -> Self {
115/// Self { map: HyperMap::new() }
116/// }
117///
118/// pub fn set_hash(&mut self, key: String, value: String) {
119/// self.map.insert(key, value);
120/// }
121/// }
122/// ```
123///
124/// The methods of the marked impl by `#[contract]` will be considered as contract methods,
125/// note that:
126/// - impl methods must have one init method with signature `fn new() -> Self`
127/// - the method with `pub` can be called by user, and other can not
128/// - no `pub` method can not be cross-called, and will no generate abi
129///
130#[cfg(any(not(feature = "advance"), doc))]
131#[proc_macro_attribute]
132pub fn contract(_attr: TokenStream, item: TokenStream) -> TokenStream {
133 // parse item content
134 let c_impl = parse_macro_input!(item as ItemImpl);
135
136 let deploy_method = contract::generate_deploy(&c_impl);
137 let invoke_method = contract::generate_invoke(&c_impl);
138 let abi_method = common::generate_abi(&c_impl);
139
140 let res = quote! {
141 #c_impl
142 // the other methods
143 #deploy_method
144 #invoke_method
145 #abi_method
146 };
147 res.into()
148}
149
150/// Mark method implementation as advance contract method
151///
152/// ## Example
153/// ``` no_run
154/// # #[cfg(feature = "advance")]
155/// {
156/// use fvm_std::advance as ledger;
157/// use fvm_macros::advance_contract;
158///
159/// pub struct SetHash {}
160///
161/// #[advance_contract]
162/// impl SetHash {
163/// fn new() {}
164///
165/// pub fn set_hash(key: String, value: String) {
166/// ledger::storage_write(key.as_bytes(), value.as_bytes());
167/// }
168/// // other methods...
169/// }
170/// }
171/// ```
172///
173/// The methods of the marked impl by `#[advance_contract]` will be considered as contract methods,
174/// and the use of `fvm_std` should be in advance mode, note that:
175/// - this macro can only used in `advance` mode
176/// - impl methods must have one init method with signature `fn new()`
177/// - the method with `pub` can be called by user, and other can not
178/// - no `pub` method can not be cross-called, and will no generate abi
179///
180#[cfg(any(feature = "advance", doc))]
181#[proc_macro_attribute]
182pub fn advance_contract(_attr: TokenStream, item: TokenStream) -> TokenStream {
183 // parse item content
184 let c_impl = parse_macro_input!(item as ItemImpl);
185
186 let deploy_method = advance::generate_deploy(&c_impl);
187 let invoke_method = advance::generate_invoke(&c_impl);
188 let abi_method = common::generate_abi(&c_impl);
189
190 let res = quote! {
191 #c_impl
192 // the other methods
193 #deploy_method
194 #invoke_method
195 #abi_method
196 };
197 res.into()
198}
199
200/// Generate cross call attribute
201/// this macro has two ways to use.
202/// - First cross call by address.
203///
204/// ## Example
205///
206/// ```no_run
207/// use fvm_std::prelude::{String, Address};
208/// use fvm_macros::cross;
209/// # #[cfg(not(feature = "advance"))]
210///
211/// #[cross("0x1000203012030120302130210321000000000000")]
212/// trait SimulateBank {
213/// fn transfer_value(addr1: &Address, addr2: Address, amount: u64) -> bool;
214/// fn get_account_balance(addr: Address) -> u64;
215/// }
216/// ```
217/// - Second cross call by cns, attribute with `cns_name`.
218///
219/// ## Example
220///
221/// ```no_run
222/// use fvm_std::prelude::{String, Address};
223/// use fvm_macros::cross;
224///
225/// # #[cfg(not(feature = "advance"))]
226/// #[cross(cns_name="HelloContract")]
227/// trait SimulateBankCNS {
228/// fn transfer_value(addr1: &Address, addr2: Address, amount: u64) -> bool;
229/// fn get_account_balance(addr: Address) -> u64;
230/// }
231/// ```
232///
233#[proc_macro_attribute]
234pub fn cross(attr: TokenStream, item: TokenStream) -> TokenStream {
235 match cross::generate_cross_call(attr.into(), item.into()) {
236 Ok(token) => token.into(),
237 Err(e) => e.into_compile_error().into()
238 }
239}
240
241/// Parallel level 2
242/// This attribute could have two fields. `field_name` and `para_index`.
243/// `field_name` point out which StoreField will parallel
244/// `para_index` the para index of this StoreField, used for `HyperMap`
245/// ```rust
246/// # #[cfg(not(feature = "advance"))]
247/// {
248/// use fvm_std::collections::hyper_map::HyperMap;
249/// use fvm_macros::parallel_field;
250/// #[parallel_field]
251/// fn demo(key: String) {}
252///
253/// #[parallel_field(field_name="key")]
254/// fn demo1(key: String) {}
255///
256/// #[parallel_field(field_name="key")]
257/// #[parallel_field(field_name="key2", para_index="1")]
258/// fn demo2(key: String, key2: HyperMap<String, String>) {}
259/// }
260/// ```
261#[proc_macro_attribute]
262pub fn parallel_field(_attr: TokenStream, item: TokenStream) -> TokenStream {
263 item
264}
265
266/// Parallel level 1
267#[proc_macro_attribute]
268pub fn parallel_contract(_attr: TokenStream, item: TokenStream) -> TokenStream {
269 item
270}
271
272/// Used while cross call the parallel
273/// the attribute has three fields.
274/// `address`: cross contract address
275/// `address_index`: if `address` is empty, then you should declare the cross address in function params, this is the index of params.
276/// `methods`: the name of cross call the methods
277/// `cns_name`: CNS of contract.
278/// `cns_index`: CNS of contract index.
279/// ```rust
280/// use fvm_std::types::Address;
281/// use fvm_macros::parallel_cross;
282///
283/// #[parallel_cross(address="0x0", methods="call")]
284/// fn demo1() {}
285///
286/// #[parallel_cross(address_index=1, methods="call")]
287/// fn demo2(addr: Address) {}
288///
289/// #[parallel_cross(cns_name="Demo", methods="call")]
290/// fn demo3(addr: Address) {}
291///
292/// #[parallel_cross(cns_index=1, methods="call")]
293/// fn demo4(addr: Address) {}
294/// ```
295#[proc_macro_attribute]
296pub fn parallel_cross(_attr: TokenStream, item: TokenStream) -> TokenStream {
297 item
298}
299
300
301/// Mark parallel call method
302///
303/// This attribute used for call the contract method, while the called method has parallel info.
304/// ```rust
305/// use fvm_macros::parallel_call_method;
306/// use fvm_macros::parallel_field;
307///
308/// #[parallel_field(field_name="key1")]
309/// #[parallel_call_method(methods="method2")]
310/// fn method1(){
311/// method2();
312/// }
313///
314/// #[parallel_field(field_name="key")]
315/// fn method2(){}
316/// ```
317#[proc_macro_attribute]
318pub fn parallel_call_method(_attr: TokenStream, item: TokenStream) -> TokenStream {
319 item
320}
321
322