1#![crate_type = "proc-macro"]
2#![allow(clippy::manual_unwrap_or_default)] use proc_macro::TokenStream;
5use proc_macro_error::abort;
6use proc_macro_error::abort_call_site;
7use proc_macro_error::proc_macro_error;
8use quote::TokenStreamExt;
9use syn::parse::Parse;
10use syn::parse::ParseStream;
11use syn::parse::Result;
12use syn::punctuated::Punctuated;
13use syn::spanned::Spanned;
14use util::get_return_type_ident;
15use util::is_extern_result_callback_result;
16
17mod dna_properties;
18mod entry_helper;
19mod entry_type_registration;
20mod entry_types;
21mod entry_types_conversions;
22mod entry_types_name_registration;
23mod entry_zomes;
24mod link_types;
25mod link_zomes;
26mod to_coordinates;
27mod unit_enum;
28mod util;
29
30struct EntryDef(holochain_integrity_types::entry_def::EntryDef);
31struct EntryDefId(holochain_integrity_types::entry_def::EntryDefId);
32struct EntryVisibility(holochain_integrity_types::entry_def::EntryVisibility);
33struct RequiredValidations(holochain_integrity_types::entry_def::RequiredValidations);
34
35impl Parse for EntryDef {
36 fn parse(input: ParseStream) -> Result<Self> {
37 let mut id =
38 holochain_integrity_types::entry_def::EntryDefId::App(String::default().into());
39 let mut required_validations =
40 holochain_integrity_types::entry_def::RequiredValidations::default();
41 let mut visibility = holochain_integrity_types::entry_def::EntryVisibility::default();
42
43 let vars = Punctuated::<syn::MetaNameValue, syn::Token![,]>::parse_terminated(input)?;
44 for var in vars {
45 if let Some(segment) = var.path.segments.first() {
46 match segment.ident.to_string().as_str() {
47 "id" => match var.lit {
48 syn::Lit::Str(s) => {
49 id = holochain_integrity_types::entry_def::EntryDefId::App(
50 s.value().to_string().into(),
51 )
52 }
53 _ => unreachable!(),
54 },
55 "required_validations" => match var.lit {
56 syn::Lit::Int(i) => {
57 required_validations =
58 holochain_integrity_types::entry_def::RequiredValidations::from(
59 i.base10_parse::<u8>()?,
60 )
61 }
62 _ => unreachable!(),
63 },
64 "visibility" => {
65 match var.lit {
66 syn::Lit::Str(s) => visibility = match s.value().as_str() {
67 "public" => {
68 holochain_integrity_types::entry_def::EntryVisibility::Public
69 }
70 "private" => {
71 holochain_integrity_types::entry_def::EntryVisibility::Private
72 }
73 _ => unreachable!(),
74 },
75 _ => unreachable!(),
76 };
77 }
78 _ => {}
79 }
80 }
81 }
82 Ok(EntryDef(holochain_integrity_types::entry_def::EntryDef {
83 id,
84 visibility,
85 required_validations,
86 cache_at_agent_activity: false,
87 }))
88 }
89}
90
91impl quote::ToTokens for EntryDefId {
92 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
93 match &self.0 {
94 holochain_integrity_types::entry_def::EntryDefId::App(s) => {
95 let string: String = s.0.to_string();
96 tokens.append_all(quote::quote! {
97 hdi::prelude::EntryDefId::App(#string.into())
98 });
99 }
100 _ => unreachable!(),
101 }
102 }
103}
104
105impl quote::ToTokens for RequiredValidations {
106 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
107 let u = <u8>::from(self.0);
108 tokens.append_all(quote::quote! {
109 hdi::prelude::RequiredValidations::from(#u)
110 });
111 }
112}
113
114impl quote::ToTokens for EntryVisibility {
115 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
116 let variant = syn::Ident::new(
117 match self.0 {
118 holochain_integrity_types::entry_def::EntryVisibility::Public => "Public",
119 holochain_integrity_types::entry_def::EntryVisibility::Private => "Private",
120 },
121 proc_macro2::Span::call_site(),
122 );
123 tokens.append_all(quote::quote! {
124 hdi::prelude::EntryVisibility::#variant
125 });
126 }
127}
128
129impl quote::ToTokens for EntryDef {
130 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
131 let id = EntryDefId(self.0.id.clone());
132 let visibility = EntryVisibility(self.0.visibility);
133 let required_validations = RequiredValidations(self.0.required_validations);
134
135 tokens.append_all(quote::quote! {
136 hdi::prelude::EntryDef {
137 id: #id,
138 visibility: #visibility,
139 required_validations: #required_validations,
140 }
141 });
142 }
143}
144
145#[proc_macro_error]
146#[proc_macro_attribute]
147pub fn hdk_extern(attrs: TokenStream, item: TokenStream) -> TokenStream {
148 let mut item_fn = syn::parse_macro_input!(item as syn::ItemFn);
150
151 let fn_name = item_fn.sig.ident.to_string();
152 let is_infallible = attrs.to_string() == "infallible";
153
154 if let syn::ReturnType::Type(_, ref ty) = item_fn.sig.output {
156 const EXTERN_RESULT: &str = "ExternResult";
157 const VALIDATE_CALLBACK_RESULT: &str = "ValidateCallbackResult";
158 const INIT_CALLBACK_RESULT: &str = "InitCallbackResult";
159
160 match (fn_name.as_str(), get_return_type_ident(ty)) {
161 ("validate" | "genesis_self_check", Some(return_type)) => {
162 if is_infallible && return_type != VALIDATE_CALLBACK_RESULT {
163 abort!(
164 ty.span(),
165 "`{}` must return `{}`",
166 fn_name,
167 VALIDATE_CALLBACK_RESULT
168 );
169 } else if !is_infallible
170 && !is_extern_result_callback_result(ty, VALIDATE_CALLBACK_RESULT)
171 {
172 abort!(
173 ty.span(),
174 "`{}` must return `{}<{}>`",
175 fn_name,
176 EXTERN_RESULT,
177 VALIDATE_CALLBACK_RESULT
178 );
179 }
180 }
181 ("init", Some(return_type)) => {
182 if is_infallible && return_type != INIT_CALLBACK_RESULT {
183 abort!(
184 ty.span(),
185 "`{}` must return `{}`",
186 fn_name,
187 INIT_CALLBACK_RESULT
188 );
189 } else if !is_infallible
190 && !is_extern_result_callback_result(ty, INIT_CALLBACK_RESULT)
191 {
192 abort!(
193 ty.span(),
194 "`{}` must return `{}<{}>`",
195 fn_name,
196 EXTERN_RESULT,
197 INIT_CALLBACK_RESULT
198 );
199 }
200 }
201 ("post_commit", r) => {
202 let type_str = quote::quote!(#ty).to_string();
203
204 if r.is_some() && is_infallible {
205 abort!(
206 ty.span(),
207 "`{}` must not have a return type", fn_name;
208 help = "remove the `{}` return type", type_str
209 );
210 } else if !is_extern_result_callback_result(ty, "()") {
211 abort!(
212 ty.span(),
213 "`{}` must return `{}<{}>`",
214 fn_name,
215 EXTERN_RESULT,
216 "()"
217 );
218 }
219 }
220 (_, Some(return_type)) => {
221 let type_str = quote::quote!(#ty).to_string();
222
223 if is_infallible && return_type == EXTERN_RESULT {
224 abort!(
225 ty.span(),
226 "functions marked as infallible must return the inner type directly"
227 );
228 } else if !is_infallible && return_type != EXTERN_RESULT {
229 abort!(
230 ty.span(),
231 "functions marked with #[hdk_extern] must return `{}` instead of `{}`", EXTERN_RESULT, type_str;
232 help = "change the return type to `{}<{}>` or mark the function as infallible if it cannot fail #[hdk_extern(infallible)]", EXTERN_RESULT, type_str
233 );
234 }
235 }
236 _ => {}
237 }
238 }
239
240 let external_fn_ident = item_fn.sig.ident.clone();
243 if item_fn.sig.inputs.len() > 1 {
244 abort_call_site!("hdk_extern functions must take a single parameter or none");
245 }
246 let input_type = if let Some(syn::FnArg::Typed(pat_type)) = item_fn.sig.inputs.first() {
247 pat_type.ty.clone()
248 } else {
249 let param_type = syn::Type::Verbatim(quote::quote! { () });
250 let param_pat = syn::Pat::Wild(syn::PatWild {
251 underscore_token: syn::token::Underscore::default(),
252 attrs: Vec::new(),
253 });
254 let param = syn::FnArg::Typed(syn::PatType {
255 attrs: Vec::new(),
256 pat: Box::new(param_pat),
257 colon_token: syn::token::Colon::default(),
258 ty: Box::new(param_type.clone()),
259 });
260 item_fn.sig.inputs.push(param);
261 Box::new(param_type)
262 };
263 let output_type = if let syn::ReturnType::Type(_, ref ty) = item_fn.sig.output {
264 ty.clone()
265 } else {
266 Box::new(syn::Type::Verbatim(quote::quote! { () }))
267 };
268
269 let internal_fn_ident = external_fn_ident.clone();
270
271 if is_infallible {
272 (quote::quote! {
273 map_extern_infallible!(#external_fn_ident, #internal_fn_ident, #input_type, #output_type);
274 #item_fn
275 })
276 .into()
277 } else {
278 (quote::quote! {
279 map_extern!(#external_fn_ident, #internal_fn_ident, #input_type, #output_type);
280 #item_fn
281 })
282 .into()
283 }
284}
285
286#[proc_macro_error]
287#[proc_macro_derive(EntryDefRegistration, attributes(entry_type))]
288pub fn derive_entry_type_registration(input: TokenStream) -> TokenStream {
289 entry_type_registration::derive(input)
290}
291
292#[proc_macro_error]
293#[proc_macro_derive(UnitEnum, attributes(unit_enum, unit_attrs))]
294pub fn derive_to_unit_enum(input: TokenStream) -> TokenStream {
295 unit_enum::derive(input)
296}
297
298#[proc_macro_error]
324#[proc_macro_attribute]
325pub fn hdk_entry_types(attrs: TokenStream, code: TokenStream) -> TokenStream {
326 entry_types::build(attrs, code)
327}
328
329#[proc_macro_error]
331#[proc_macro_attribute]
332pub fn hdk_link_types(attrs: TokenStream, code: TokenStream) -> TokenStream {
333 link_types::build(attrs, code)
334}
335
336#[proc_macro_error]
337#[proc_macro_attribute]
338pub fn hdk_to_coordinates(attrs: TokenStream, code: TokenStream) -> TokenStream {
339 to_coordinates::build(attrs, code)
340}
341
342#[proc_macro_error]
343#[proc_macro_attribute]
344pub fn hdk_entry_types_name_registration(attrs: TokenStream, code: TokenStream) -> TokenStream {
345 entry_types_name_registration::build(attrs, code)
346}
347
348#[proc_macro_error]
349#[proc_macro_attribute]
350pub fn hdk_entry_types_conversions(attrs: TokenStream, code: TokenStream) -> TokenStream {
351 entry_types_conversions::build(attrs, code)
352}
353
354#[proc_macro_error]
355#[proc_macro_attribute]
356pub fn hdk_dependent_entry_types(attrs: TokenStream, code: TokenStream) -> TokenStream {
357 entry_zomes::build(attrs, code)
358}
359
360#[proc_macro_error]
361#[proc_macro_attribute]
362pub fn hdk_dependent_link_types(attrs: TokenStream, code: TokenStream) -> TokenStream {
363 link_zomes::build(attrs, code)
364}
365
366#[proc_macro_error]
378#[proc_macro_attribute]
379pub fn hdk_entry_helper(attrs: TokenStream, code: TokenStream) -> TokenStream {
380 entry_helper::build(attrs, code)
381}
382
383#[proc_macro_error]
400#[proc_macro_attribute]
401pub fn dna_properties(attrs: TokenStream, code: TokenStream) -> TokenStream {
402 dna_properties::build(attrs, code)
403}