#![doc = "This crate provides a set of macros and derive attributes to simplify the process of writing"]
#![doc = "smart contracts for the Odra platform."]
#![feature(box_patterns)]
use crate::utils::IntoCode;
use ast::*;
use ir::{ModuleImplIR, ModuleStructIR, TypeIR};
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
mod ast;
mod ir;
#[cfg(test)]
mod test_utils;
mod utils;
macro_rules! span_error {
($span:ident, $msg:expr) => {
syn::Error::new(syn::spanned::Spanned::span(&$span), $msg)
.to_compile_error()
.into()
};
}
#[proc_macro_attribute]
pub fn module(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr: TokenStream2 = attr.into();
let item: TokenStream2 = item.into();
if let Ok(ir) = ModuleImplIR::try_from((&attr, &item)) {
return ModuleImplItem::try_from(&ir).into_code();
}
if let Ok(ir) = ModuleStructIR::try_from((&attr, &item)) {
return ModuleStructItem::try_from(&ir).into_code();
}
span_error!(item, "Struct or impl block expected")
}
#[proc_macro_attribute]
pub fn factory(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr: TokenStream2 = attr.into();
let item: TokenStream2 = item.into();
if let Ok(ir) = ModuleStructIR::try_from((&attr, &item)) {
return FactoryModuleStructItem::try_from(&ir).into_code();
}
if let Ok(ir) = ModuleImplIR::try_from((&attr, &item)) {
return FactoryModuleImplItem::try_from(&ir).into_code();
}
span_error!(item, "Struct or impl block expected")
}
#[proc_macro_attribute]
pub fn odra_type(_attr: TokenStream, item: TokenStream) -> TokenStream {
let item = item.into();
if let Ok(ir) = TypeIR::try_from(&item) {
return OdraTypeAttrItem::try_from(&ir).into_code();
}
span_error!(item, "Struct or Enum expected")
}
#[proc_macro_attribute]
pub fn odra_error(_attr: TokenStream, item: TokenStream) -> TokenStream {
let item = item.into();
if let Ok(ir) = TypeIR::try_from(&item) {
return OdraErrorAttrItem::try_from(&ir).into_code();
}
span_error!(item, "Struct or Enum expected")
}
#[proc_macro_attribute]
pub fn external_contract(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr: TokenStream2 = attr.into();
let item: TokenStream2 = item.into();
if syn::parse2::<syn::ItemImpl>(item.clone()).is_ok() {
return span_error!(
attr,
"#[external_contract] can be only applied to trait only"
);
}
match ModuleImplIR::try_from((&attr, &item)) {
Ok(ir) => ExternalContractImpl::try_from(&ir).into_code(),
Err(e) => e.to_compile_error().into()
}
}
#[proc_macro_attribute]
pub fn event(_attr: TokenStream, input: TokenStream) -> TokenStream {
let input: TokenStream2 = input.into();
OdraEventItem::try_from(&input).into_code()
}
#[proc_macro_derive(IntoRuntimeArgs)]
pub fn derive_into_runtime_args(item: TokenStream) -> TokenStream {
let item = syn::parse_macro_input!(item as syn::DeriveInput);
let args_ident = syn::Ident::new("args", item.ident.span());
match item {
syn::DeriveInput {
ident,
data: syn::Data::Struct(syn::DataStruct { fields, .. }),
..
} => {
let fields = fields
.into_iter()
.map(|f| {
let name = f.ident.unwrap();
quote::quote!(odra::args::EntrypointArgument::insert_runtime_arg(self.#name, stringify!(#name), &mut #args_ident);)
})
.collect::<proc_macro2::TokenStream>();
let res = quote::quote! {
impl Into<odra::casper_types::RuntimeArgs> for #ident {
fn into(self) -> odra::casper_types::RuntimeArgs {
let mut #args_ident = odra::casper_types::RuntimeArgs::new();
#fields
#args_ident
}
}
};
res.into()
}
_ => panic!("Struct expected")
}
}