fvm-macros 1.0.0

provide the macros for user to write contract more convenient
Documentation
//! Provides the macros needed to write fvm contracts
//!
//! ## Basic Usage
//! Macros can be used in contract for simplify contract preparation process,
//! like this:
//! ``` no_run
//! # #[cfg(not(feature = "advance"))]
//! use fvm_macros::contract;
//! # #[cfg(not(feature = "advance"))]
//! use fvm_macros::storage;
//! # #[cfg(not(feature = "advance"))]
//! use fvm_std::collections::hyper_map::HyperMap;
//!
//! # #[cfg(not(feature = "advance"))]
//! #[storage]
//! pub struct SetHash {
//!     map: HyperMap<String, String>,
//! }
//!
//! # #[cfg(not(feature = "advance"))]
//! #[contract]
//! impl SetHash {
//!     fn new() -> Self {
//!         Self { map: HyperMap::new() }
//!     }
//!
//!     pub fn set_hash(&mut self, key: String, value: String) {
//!         self.map.insert(key, value);
//!     }
//!
//!     pub fn get_hash(&mut self, key: String) -> &String {
//!         self.map.get(&key).unwrap()
//!     }
//! }
//! ```
//!
//! ## Contract Mode
//! Now write contract have two mode:
//! - **normal mode**
//!
//! Contract with normal mode limited data storage format, and user can write contract more
//! convenient. User can use all macros except `advance_contract`.
//! - **advance mode**
//!
//! Contract with advance mode not limited data storage format, and it must open advance
//! feature with `fvm-std` and 'fvm-macros' lib to use this mode. The execute speed of contract
//! in this mode would be fast then the contract in normal mode.
//!

use proc_macro::{TokenStream};
use quote::{
    quote,
};

use syn::{parse_macro_input};
use syn::ItemImpl;

#[cfg(any(not(feature = "advance"), doc))]
mod storage;
#[cfg(any(not(feature = "advance"), doc))]
mod contract;
mod common;
mod cross;
#[cfg(feature = "advance")]
mod advance;

/// Generate storage attribute for struct
///
/// ## Example
/// ``` no_run
/// # #[cfg(not(feature = "advance"))]
/// use fvm_macros::storage;
/// # #[cfg(not(feature = "advance"))]
/// use fvm_std::collections::hyper_map::HyperMap;
///
/// # #[cfg(not(feature = "advance"))]
/// #[storage]
/// pub struct SetHash {
///     map: HyperMap<String, String>,
/// }
/// ```
///
/// The attributes of the marked struct by `#[storage]` will be automatically
/// mapped to the ledger
///
#[cfg(any(not(feature = "advance"), doc))]
#[proc_macro_attribute]
pub fn storage(attr: TokenStream, item: TokenStream) -> TokenStream {
    //实现SpreadLayout接口
    storage::generate_storage(attr.into(), item.into()).into()
}


/// Mark method implementation as contract method
///
/// ## Example
/// ``` no_run
/// # #[cfg(not(feature = "advance"))]
/// use fvm_macros::contract;
/// # #[cfg(not(feature = "advance"))]
/// use fvm_macros::storage;
/// # #[cfg(not(feature = "advance"))]
/// use fvm_std::collections::hyper_map::HyperMap;
///
/// # #[cfg(not(feature = "advance"))]
/// #[storage]
/// pub struct SetHash {
///     map: HyperMap<String, String>,
/// }
///
/// # #[cfg(not(feature = "advance"))]
/// #[contract]
/// impl SetHash {
///     fn new() -> Self {
///         Self { map: HyperMap::new() }
///     }
///
///     pub fn set_hash(&mut self, key: String, value: String) {
///         self.map.insert(key, value);
///     }
/// }
/// ```
///
/// The methods of the marked impl by `#[contract]` will be considered as contract methods,
/// note that:
/// - impl methods must have one init method with signature `fn new() -> Self`
/// - the method with `pub` can be called by user, and other can not
/// - no `pub` method can not be cross-called, and will no generate abi
///
#[cfg(any(not(feature = "advance"), doc))]
#[proc_macro_attribute]
pub fn contract(_attr: TokenStream, item: TokenStream) -> TokenStream {
    // parse item content
    let c_impl = parse_macro_input!(item as ItemImpl);

    let deploy_method = contract::generate_deploy(&c_impl);
    let invoke_method = contract::generate_invoke(&c_impl);
    let abi_method = common::generate_abi(&c_impl);

    let res = quote! {
        #c_impl
        // the other methods
        #deploy_method
        #invoke_method
        #abi_method
    };
    res.into()
}

/// Mark method implementation as advance contract method
///
/// ## Example
/// ``` no_run
/// # #[cfg(feature = "advance")]
/// {
/// use fvm_std::advance as ledger;
/// use fvm_macros::advance_contract;
///
/// pub struct SetHash {}
///
/// #[advance_contract]
/// impl SetHash {
///     fn new() {}
///
///     pub fn set_hash(key: String, value: String) {
///         ledger::storage_write(key.as_bytes(), value.as_bytes());
///     }
///     // other methods...
/// }
/// }
/// ```
///
/// The methods of the marked impl by `#[advance_contract]` will be considered as contract methods,
/// and the use of `fvm_std` should be in advance mode, note that:
/// - this macro can only used in `advance` mode
/// - impl methods must have one init method with signature `fn new()`
/// - the method with `pub` can be called by user, and other can not
/// - no `pub` method can not be cross-called, and will no generate abi
///
#[cfg(any(feature = "advance", doc))]
#[proc_macro_attribute]
pub fn advance_contract(_attr: TokenStream, item: TokenStream) -> TokenStream {
    // parse item content
    let c_impl = parse_macro_input!(item as ItemImpl);

    let deploy_method = advance::generate_deploy(&c_impl);
    let invoke_method = advance::generate_invoke(&c_impl);
    let abi_method = common::generate_abi(&c_impl);

    let res = quote! {
        #c_impl
        // the other methods
        #deploy_method
        #invoke_method
        #abi_method
    };
    res.into()
}

/// Generate cross call attribute
/// this macro has two ways to use.
/// - First cross call by address.
///
/// ## Example
///
/// ```no_run
/// use fvm_std::prelude::{String, Address};
/// use fvm_macros::cross;
/// # #[cfg(not(feature = "advance"))]
///
/// #[cross("0x1000203012030120302130210321000000000000")]
/// trait SimulateBank {
///     fn transfer_value(addr1: &Address, addr2: Address, amount: u64) -> bool;
///     fn get_account_balance(addr: Address) -> u64;
/// }
/// ```
/// - Second cross call by cns, attribute with `cns_name`.
///
/// ## Example
///
/// ```no_run
/// use fvm_std::prelude::{String, Address};
/// use fvm_macros::cross;
///
/// # #[cfg(not(feature = "advance"))]
/// #[cross(cns_name="HelloContract")]
/// trait SimulateBankCNS {
///     fn transfer_value(addr1: &Address, addr2: Address, amount: u64) -> bool;
///     fn get_account_balance(addr: Address) -> u64;
/// }
/// ```
///
#[proc_macro_attribute]
pub fn cross(attr: TokenStream, item: TokenStream) -> TokenStream {
    match cross::generate_cross_call(attr.into(), item.into()) {
        Ok(token) => token.into(),
        Err(e) => e.into_compile_error().into()
    }
}

/// Parallel level 2
/// This attribute could have two fields. `field_name` and `para_index`.
/// `field_name` point out which StoreField will parallel
/// `para_index` the para index of this StoreField, used for `HyperMap`
/// ```rust
///  # #[cfg(not(feature = "advance"))]
///  {
///  use fvm_std::collections::hyper_map::HyperMap;
///  use fvm_macros::parallel_field;
///  #[parallel_field]
///  fn demo(key: String) {}
///
///  #[parallel_field(field_name="key")]
///  fn demo1(key: String) {}
///
///  #[parallel_field(field_name="key")]
///  #[parallel_field(field_name="key2", para_index="1")]
///  fn demo2(key: String, key2: HyperMap<String, String>) {}
/// }
/// ```
#[proc_macro_attribute]
pub fn parallel_field(_attr: TokenStream, item: TokenStream) -> TokenStream {
    item
}

/// Parallel level 1
#[proc_macro_attribute]
pub fn parallel_contract(_attr: TokenStream, item: TokenStream) -> TokenStream {
    item
}

/// Used while cross call the parallel
/// the attribute has three fields.
/// `address`: cross contract address
/// `address_index`: if `address` is empty, then you should declare the cross address in function params, this is the index of params.
/// `methods`: the name of cross call the methods
/// `cns_name`: CNS of contract.
/// `cns_index`: CNS of contract index.
/// ```rust
/// use fvm_std::types::Address;
/// use fvm_macros::parallel_cross;
///
/// #[parallel_cross(address="0x0", methods="call")]
/// fn demo1() {}
///
/// #[parallel_cross(address_index=1, methods="call")]
/// fn demo2(addr: Address) {}
///
/// #[parallel_cross(cns_name="Demo", methods="call")]
/// fn demo3(addr: Address) {}
///
/// #[parallel_cross(cns_index=1, methods="call")]
/// fn demo4(addr: Address) {}
/// ```
#[proc_macro_attribute]
pub fn parallel_cross(_attr: TokenStream, item: TokenStream) -> TokenStream {
    item
}


/// Mark parallel call method
///
/// This attribute used for call the contract method, while the called method has parallel info.
/// ```rust
/// use fvm_macros::parallel_call_method;
/// use fvm_macros::parallel_field;
///
/// #[parallel_field(field_name="key1")]
/// #[parallel_call_method(methods="method2")]
/// fn method1(){
///     method2();
/// }
///
/// #[parallel_field(field_name="key")]
/// fn method2(){}
/// ```
#[proc_macro_attribute]
pub fn parallel_call_method(_attr: TokenStream, item: TokenStream) -> TokenStream {
    item
}