1#![crate_type = "proc-macro"]
2
3use proc_macro::TokenStream;
4use proc_macro_error::abort;
5use proc_macro_error::abort_call_site;
6use proc_macro_error::proc_macro_error;
7use syn::spanned::Spanned;
8use util::get_return_type_ident;
9use util::is_extern_result_callback_result;
10
11mod dna_properties;
12mod entry_helper;
13mod entry_type_registration;
14mod entry_types;
15mod entry_types_conversions;
16mod entry_types_name_registration;
17mod entry_zomes;
18mod link_types;
19mod link_zomes;
20mod to_coordinates;
21mod unit_enum;
22mod util;
23
24#[proc_macro_error]
25#[proc_macro_attribute]
26pub fn hdk_extern(attrs: TokenStream, item: TokenStream) -> TokenStream {
27 let mut item_fn = syn::parse_macro_input!(item as syn::ItemFn);
29
30 let fn_name = item_fn.sig.ident.to_string();
31 let is_infallible = attrs.to_string() == "infallible";
32
33 if let syn::ReturnType::Type(_, ref ty) = item_fn.sig.output {
35 const EXTERN_RESULT: &str = "ExternResult";
36 const VALIDATE_CALLBACK_RESULT: &str = "ValidateCallbackResult";
37 const INIT_CALLBACK_RESULT: &str = "InitCallbackResult";
38
39 match (fn_name.as_str(), get_return_type_ident(ty)) {
40 ("validate" | "genesis_self_check", Some(return_type)) => {
41 if is_infallible && return_type != VALIDATE_CALLBACK_RESULT {
42 abort!(
43 ty.span(),
44 "`{}` must return `{}`",
45 fn_name,
46 VALIDATE_CALLBACK_RESULT
47 );
48 } else if !is_infallible
49 && !is_extern_result_callback_result(ty, VALIDATE_CALLBACK_RESULT)
50 {
51 abort!(
52 ty.span(),
53 "`{}` must return `{}<{}>`",
54 fn_name,
55 EXTERN_RESULT,
56 VALIDATE_CALLBACK_RESULT
57 );
58 }
59 }
60 ("init", Some(return_type)) => {
61 if is_infallible && return_type != INIT_CALLBACK_RESULT {
62 abort!(
63 ty.span(),
64 "`{}` must return `{}`",
65 fn_name,
66 INIT_CALLBACK_RESULT
67 );
68 } else if !is_infallible
69 && !is_extern_result_callback_result(ty, INIT_CALLBACK_RESULT)
70 {
71 abort!(
72 ty.span(),
73 "`{}` must return `{}<{}>`",
74 fn_name,
75 EXTERN_RESULT,
76 INIT_CALLBACK_RESULT
77 );
78 }
79 }
80 ("post_commit", r) => {
81 let type_str = quote::quote!(#ty).to_string();
82
83 if r.is_some() && is_infallible {
84 abort!(
85 ty.span(),
86 "`{}` must not have a return type", fn_name;
87 help = "remove the `{}` return type", type_str
88 );
89 } else if !is_extern_result_callback_result(ty, "()") {
90 abort!(
91 ty.span(),
92 "`{}` must return `{}<{}>`",
93 fn_name,
94 EXTERN_RESULT,
95 "()"
96 );
97 }
98 }
99 (_, Some(return_type)) => {
100 let type_str = quote::quote!(#ty).to_string();
101
102 if is_infallible && return_type == EXTERN_RESULT {
103 abort!(
104 ty.span(),
105 "functions marked as infallible must return the inner type directly"
106 );
107 } else if !is_infallible && return_type != EXTERN_RESULT {
108 abort!(
109 ty.span(),
110 "functions marked with #[hdk_extern] must return `{}` instead of `{}`", EXTERN_RESULT, type_str;
111 help = "change the return type to `{}<{}>` or mark the function as infallible if it cannot fail #[hdk_extern(infallible)]", EXTERN_RESULT, type_str
112 );
113 }
114 }
115 _ => {}
116 }
117 }
118
119 let external_fn_ident = item_fn.sig.ident.clone();
122 if item_fn.sig.inputs.len() > 1 {
123 abort_call_site!("hdk_extern functions must take a single parameter or none");
124 }
125 let input_type = if let Some(syn::FnArg::Typed(pat_type)) = item_fn.sig.inputs.first() {
126 pat_type.ty.clone()
127 } else {
128 let param_type = syn::Type::Verbatim(quote::quote! { () });
129 let param_pat = syn::Pat::Wild(syn::PatWild {
130 underscore_token: syn::token::Underscore::default(),
131 attrs: Vec::new(),
132 });
133 let param = syn::FnArg::Typed(syn::PatType {
134 attrs: Vec::new(),
135 pat: Box::new(param_pat),
136 colon_token: syn::token::Colon::default(),
137 ty: Box::new(param_type.clone()),
138 });
139 item_fn.sig.inputs.push(param);
140 Box::new(param_type)
141 };
142 let output_type = if let syn::ReturnType::Type(_, ref ty) = item_fn.sig.output {
143 ty.clone()
144 } else {
145 Box::new(syn::Type::Verbatim(quote::quote! { () }))
146 };
147
148 let internal_fn_ident = external_fn_ident.clone();
149
150 if is_infallible {
151 (quote::quote! {
152 map_extern_infallible!(#external_fn_ident, #internal_fn_ident, #input_type, #output_type);
153 #item_fn
154 })
155 .into()
156 } else {
157 (quote::quote! {
158 map_extern!(#external_fn_ident, #internal_fn_ident, #input_type, #output_type);
159 #item_fn
160 })
161 .into()
162 }
163}
164
165#[proc_macro_error]
166#[proc_macro_derive(EntryDefRegistration, attributes(entry_type))]
167pub fn derive_entry_type_registration(input: TokenStream) -> TokenStream {
168 entry_type_registration::derive(input)
169}
170
171#[proc_macro_error]
172#[proc_macro_derive(UnitEnum, attributes(unit_enum, unit_attrs))]
173pub fn derive_to_unit_enum(input: TokenStream) -> TokenStream {
174 unit_enum::derive(input)
175}
176
177#[proc_macro_error]
203#[proc_macro_attribute]
204pub fn hdk_entry_types(attrs: TokenStream, code: TokenStream) -> TokenStream {
205 entry_types::build(attrs, code)
206}
207
208#[proc_macro_error]
210#[proc_macro_attribute]
211pub fn hdk_link_types(attrs: TokenStream, code: TokenStream) -> TokenStream {
212 link_types::build(attrs, code)
213}
214
215#[proc_macro_error]
216#[proc_macro_attribute]
217pub fn hdk_to_coordinates(attrs: TokenStream, code: TokenStream) -> TokenStream {
218 to_coordinates::build(attrs, code)
219}
220
221#[proc_macro_error]
222#[proc_macro_attribute]
223pub fn hdk_entry_types_name_registration(attrs: TokenStream, code: TokenStream) -> TokenStream {
224 entry_types_name_registration::build(attrs, code)
225}
226
227#[proc_macro_error]
228#[proc_macro_attribute]
229pub fn hdk_entry_types_conversions(attrs: TokenStream, code: TokenStream) -> TokenStream {
230 entry_types_conversions::build(attrs, code)
231}
232
233#[proc_macro_error]
234#[proc_macro_attribute]
235pub fn hdk_dependent_entry_types(attrs: TokenStream, code: TokenStream) -> TokenStream {
236 entry_zomes::build(attrs, code)
237}
238
239#[proc_macro_error]
240#[proc_macro_attribute]
241pub fn hdk_dependent_link_types(attrs: TokenStream, code: TokenStream) -> TokenStream {
242 link_zomes::build(attrs, code)
243}
244
245#[proc_macro_error]
257#[proc_macro_attribute]
258pub fn hdk_entry_helper(attrs: TokenStream, code: TokenStream) -> TokenStream {
259 entry_helper::build(attrs, code)
260}
261
262#[proc_macro_error]
279#[proc_macro_attribute]
280pub fn dna_properties(attrs: TokenStream, code: TokenStream) -> TokenStream {
281 dna_properties::build(attrs, code)
282}