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