#![crate_type = "proc-macro"]
use proc_macro::TokenStream;
use proc_macro_error::abort;
use proc_macro_error::abort_call_site;
use proc_macro_error::proc_macro_error;
use syn::spanned::Spanned;
use util::get_return_type_ident;
use util::is_extern_result_callback_result;
mod dna_properties;
mod entry_helper;
mod entry_type_registration;
mod entry_types;
mod entry_types_conversions;
mod entry_types_name_registration;
mod entry_zomes;
mod link_types;
mod link_zomes;
mod to_coordinates;
mod unit_enum;
mod util;
#[proc_macro_error]
#[proc_macro_attribute]
pub fn hdk_extern(attrs: TokenStream, item: TokenStream) -> TokenStream {
let mut item_fn = syn::parse_macro_input!(item as syn::ItemFn);
let fn_name = item_fn.sig.ident.to_string();
let is_infallible = attrs.to_string() == "infallible";
if let syn::ReturnType::Type(_, ref ty) = item_fn.sig.output {
const EXTERN_RESULT: &str = "ExternResult";
const VALIDATE_CALLBACK_RESULT: &str = "ValidateCallbackResult";
const INIT_CALLBACK_RESULT: &str = "InitCallbackResult";
match (fn_name.as_str(), get_return_type_ident(ty)) {
("validate" | "genesis_self_check", Some(return_type)) => {
if is_infallible && return_type != VALIDATE_CALLBACK_RESULT {
abort!(
ty.span(),
"`{}` must return `{}`",
fn_name,
VALIDATE_CALLBACK_RESULT
);
} else if !is_infallible
&& !is_extern_result_callback_result(ty, VALIDATE_CALLBACK_RESULT)
{
abort!(
ty.span(),
"`{}` must return `{}<{}>`",
fn_name,
EXTERN_RESULT,
VALIDATE_CALLBACK_RESULT
);
}
}
("init", Some(return_type)) => {
if is_infallible && return_type != INIT_CALLBACK_RESULT {
abort!(
ty.span(),
"`{}` must return `{}`",
fn_name,
INIT_CALLBACK_RESULT
);
} else if !is_infallible
&& !is_extern_result_callback_result(ty, INIT_CALLBACK_RESULT)
{
abort!(
ty.span(),
"`{}` must return `{}<{}>`",
fn_name,
EXTERN_RESULT,
INIT_CALLBACK_RESULT
);
}
}
("post_commit", r) => {
let type_str = quote::quote!(#ty).to_string();
if r.is_some() && is_infallible {
abort!(
ty.span(),
"`{}` must not have a return type", fn_name;
help = "remove the `{}` return type", type_str
);
} else if !is_extern_result_callback_result(ty, "()") {
abort!(
ty.span(),
"`{}` must return `{}<{}>`",
fn_name,
EXTERN_RESULT,
"()"
);
}
}
(_, Some(return_type)) => {
let type_str = quote::quote!(#ty).to_string();
if is_infallible && return_type == EXTERN_RESULT {
abort!(
ty.span(),
"functions marked as infallible must return the inner type directly"
);
} else if !is_infallible && return_type != EXTERN_RESULT {
abort!(
ty.span(),
"functions marked with #[hdk_extern] must return `{}` instead of `{}`", EXTERN_RESULT, type_str;
help = "change the return type to `{}<{}>` or mark the function as infallible if it cannot fail #[hdk_extern(infallible)]", EXTERN_RESULT, type_str
);
}
}
_ => {}
}
}
let external_fn_ident = item_fn.sig.ident.clone();
if item_fn.sig.inputs.len() > 1 {
abort_call_site!("hdk_extern functions must take a single parameter or none");
}
let input_type = if let Some(syn::FnArg::Typed(pat_type)) = item_fn.sig.inputs.first() {
pat_type.ty.clone()
} else {
let param_type = syn::Type::Verbatim(quote::quote! { () });
let param_pat = syn::Pat::Wild(syn::PatWild {
underscore_token: syn::token::Underscore::default(),
attrs: Vec::new(),
});
let param = syn::FnArg::Typed(syn::PatType {
attrs: Vec::new(),
pat: Box::new(param_pat),
colon_token: syn::token::Colon::default(),
ty: Box::new(param_type.clone()),
});
item_fn.sig.inputs.push(param);
Box::new(param_type)
};
let output_type = if let syn::ReturnType::Type(_, ref ty) = item_fn.sig.output {
ty.clone()
} else {
Box::new(syn::Type::Verbatim(quote::quote! { () }))
};
let internal_fn_ident = external_fn_ident.clone();
if is_infallible {
(quote::quote! {
map_extern_infallible!(#external_fn_ident, #internal_fn_ident, #input_type, #output_type);
#item_fn
})
.into()
} else {
(quote::quote! {
map_extern!(#external_fn_ident, #internal_fn_ident, #input_type, #output_type);
#item_fn
})
.into()
}
}
#[proc_macro_error]
#[proc_macro_derive(EntryDefRegistration, attributes(entry_type))]
pub fn derive_entry_type_registration(input: TokenStream) -> TokenStream {
entry_type_registration::derive(input)
}
#[proc_macro_error]
#[proc_macro_derive(UnitEnum, attributes(unit_enum, unit_attrs))]
pub fn derive_to_unit_enum(input: TokenStream) -> TokenStream {
unit_enum::derive(input)
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn hdk_entry_types(attrs: TokenStream, code: TokenStream) -> TokenStream {
entry_types::build(attrs, code)
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn hdk_link_types(attrs: TokenStream, code: TokenStream) -> TokenStream {
link_types::build(attrs, code)
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn hdk_to_coordinates(attrs: TokenStream, code: TokenStream) -> TokenStream {
to_coordinates::build(attrs, code)
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn hdk_entry_types_name_registration(attrs: TokenStream, code: TokenStream) -> TokenStream {
entry_types_name_registration::build(attrs, code)
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn hdk_entry_types_conversions(attrs: TokenStream, code: TokenStream) -> TokenStream {
entry_types_conversions::build(attrs, code)
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn hdk_dependent_entry_types(attrs: TokenStream, code: TokenStream) -> TokenStream {
entry_zomes::build(attrs, code)
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn hdk_dependent_link_types(attrs: TokenStream, code: TokenStream) -> TokenStream {
link_zomes::build(attrs, code)
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn hdk_entry_helper(attrs: TokenStream, code: TokenStream) -> TokenStream {
entry_helper::build(attrs, code)
}
#[proc_macro_error]
#[proc_macro_attribute]
pub fn dna_properties(attrs: TokenStream, code: TokenStream) -> TokenStream {
dna_properties::build(attrs, code)
}