odra_macros/
lib.rs

1#![doc = "This crate provides a set of macros and derive attributes to simplify the process of writing"]
2#![doc = "smart contracts for the Odra platform."]
3#![feature(box_patterns, result_flattening)]
4use crate::utils::IntoCode;
5use ast::*;
6use ir::{ModuleImplIR, ModuleStructIR, TypeIR};
7use proc_macro::TokenStream;
8use proc_macro2::TokenStream as TokenStream2;
9
10mod ast;
11mod ir;
12#[cfg(test)]
13mod test_utils;
14mod utils;
15
16macro_rules! span_error {
17    ($span:ident, $msg:expr) => {
18        syn::Error::new(syn::spanned::Spanned::span(&$span), $msg)
19            .to_compile_error()
20            .into()
21    };
22}
23
24/// Core element of the Odra framework, entry point for writing smart contracts.
25///
26/// Each module consists of two parts:
27/// 1. Module definition - a struct which composition of stored values (Vars and Mappings)
28///     and modules.
29/// 2. Module implementation - an implementation block.
30///
31/// The macro produces all the required code to use the module as a standalone smart contract.
32#[proc_macro_attribute]
33pub fn module(attr: TokenStream, item: TokenStream) -> TokenStream {
34    let attr: TokenStream2 = attr.into();
35    let item: TokenStream2 = item.into();
36    if let Ok(ir) = ModuleImplIR::try_from((&attr, &item)) {
37        return ModuleImplItem::try_from(&ir).into_code();
38    }
39    if let Ok(ir) = ModuleStructIR::try_from((&attr, &item)) {
40        return ModuleStructItem::try_from(&ir).into_code();
41    }
42    span_error!(item, "Struct or impl block expected")
43}
44
45/// Implements boilerplate for a type to be used in an Odra module.
46///
47/// This macro implements serialization and deserialization for the type, as well as
48/// cloning and [HasEvents](../odra_core/contract_def/trait.HasEvents.html) trait.
49#[proc_macro_attribute]
50pub fn odra_type(_attr: TokenStream, item: TokenStream) -> TokenStream {
51    let item = item.into();
52    if let Ok(ir) = TypeIR::try_from(&item) {
53        return OdraTypeAttrItem::try_from(&ir).into_code();
54    }
55    span_error!(item, "Struct or Enum expected")
56}
57
58/// Implements `Into<OdraError>` for an error enum.
59#[proc_macro_attribute]
60pub fn odra_error(_attr: TokenStream, item: TokenStream) -> TokenStream {
61    let item = item.into();
62    if let Ok(ir) = TypeIR::try_from(&item) {
63        return OdraErrorAttrItem::try_from(&ir).into_code();
64    }
65    span_error!(item, "Struct or Enum expected")
66}
67
68/// Provides implementation of a reference to an external contract.
69///
70/// If you don't have access to the contract source code, but want to call it,
71/// you can create a reference to it and interact exactly the same way as with a contract
72/// written using [macro@module] macro.
73#[proc_macro_attribute]
74pub fn external_contract(attr: TokenStream, item: TokenStream) -> TokenStream {
75    let attr: TokenStream2 = attr.into();
76    let item: TokenStream2 = item.into();
77    if let Ok(ir) = ModuleImplIR::try_from((&attr, &item)) {
78        return ExternalContractImpl::try_from(&ir).into_code();
79    }
80    span_error!(
81        item,
82        "#[external_contract] can be only applied to trait only"
83    )
84}
85
86/// This macro is used to implement the boilerplate code for the event and contract schema.
87#[proc_macro_attribute]
88pub fn event(_attr: TokenStream, input: TokenStream) -> TokenStream {
89    let input: TokenStream2 = input.into();
90    OdraEventItem::try_from(&input).into_code()
91}
92
93/// Implements `Into<odra::casper_types::RuntimeArgs>` for a struct.
94///
95/// This macro is used to convert a struct into a `RuntimeArgs` object.
96/// If applied to an enum or a union type, it will panic.
97#[proc_macro_derive(IntoRuntimeArgs)]
98pub fn derive_into_runtime_args(item: TokenStream) -> TokenStream {
99    let item = syn::parse_macro_input!(item as syn::DeriveInput);
100    let args_ident = syn::Ident::new("args", item.ident.span());
101    match item {
102        syn::DeriveInput {
103            ident,
104            data: syn::Data::Struct(syn::DataStruct { fields, .. }),
105            ..
106        } => {
107            let fields = fields
108                .into_iter()
109                .map(|f| {
110                    let name = f.ident.unwrap();
111                    quote::quote!(odra::args::EntrypointArgument::insert_runtime_arg(self.#name, stringify!(#name), &mut #args_ident);)
112                })
113                .collect::<proc_macro2::TokenStream>();
114            let res = quote::quote! {
115                impl Into<odra::casper_types::RuntimeArgs> for #ident {
116                    fn into(self) -> odra::casper_types::RuntimeArgs {
117                        let mut #args_ident = odra::casper_types::RuntimeArgs::new();
118                        #fields
119                        #args_ident
120                    }
121                }
122            };
123            res.into()
124        }
125        _ => panic!("Struct expected")
126    }
127}