|
| 1 | +use proc_macro2::{Span, TokenStream}; |
| 2 | +use quote::quote; |
| 3 | +use syn::ItemStruct; |
| 4 | + |
| 5 | +use crate::items::parameter_tokens; |
| 6 | +use crate::parse::{ |
| 7 | + hercules_path, optional_string, optional_translation_vec, take_repeated, AttrArgs, |
| 8 | +}; |
| 9 | + |
| 10 | +pub fn expand(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { |
| 11 | + let mut item_struct: ItemStruct = syn::parse2(item)?; |
| 12 | + let args = AttrArgs::parse(attr)?; |
| 13 | + let hercules = hercules_path(); |
| 14 | + |
| 15 | + let base = args.path("base")?.ok_or_else(|| { |
| 16 | + syn::Error::new( |
| 17 | + Span::call_site(), |
| 18 | + "missing required `base = SomeRuntimeFunction`", |
| 19 | + ) |
| 20 | + })?; |
| 21 | + |
| 22 | + let parameters = take_repeated(&mut item_struct, "parameter")? |
| 23 | + .iter() |
| 24 | + .map(|p| parameter_tokens(p, &hercules)) |
| 25 | + .collect::<syn::Result<Vec<_>>>()?; |
| 26 | + |
| 27 | + let identifier = optional_string(args.string("identifier"))?; |
| 28 | + let signature = optional_string(args.string("signature"))?; |
| 29 | + let name = optional_translation_vec(&args, "name", &hercules)?; |
| 30 | + let description = optional_translation_vec(&args, "description", &hercules)?; |
| 31 | + let documentation = optional_translation_vec(&args, "documentation", &hercules)?; |
| 32 | + let deprecation_message = optional_translation_vec(&args, "deprecation_message", &hercules)?; |
| 33 | + let display_message = optional_translation_vec(&args, "display_message", &hercules)?; |
| 34 | + let alias = optional_translation_vec(&args, "alias", &hercules)?; |
| 35 | + let display_icon = optional_string(args.string("display_icon"))?; |
| 36 | + let design = optional_string(args.string("design"))?; |
| 37 | + // Presence-only: this overrides `throws_error` to `true`; to leave it |
| 38 | + // unset (inherit the base), simply omit the flag. |
| 39 | + let throws_error = if args.flag("throws_error") { |
| 40 | + quote!(Some(true)) |
| 41 | + } else { |
| 42 | + quote!(None) |
| 43 | + }; |
| 44 | + |
| 45 | + let ident = &item_struct.ident; |
| 46 | + Ok(quote! { |
| 47 | + #item_struct |
| 48 | + |
| 49 | + impl #hercules::Function for #ident { |
| 50 | + type Base = #base; |
| 51 | + |
| 52 | + fn meta() -> #hercules::FunctionMeta { |
| 53 | + #hercules::FunctionMeta { |
| 54 | + identifier: #identifier, |
| 55 | + signature: #signature, |
| 56 | + name: #name, |
| 57 | + description: #description, |
| 58 | + documentation: #documentation, |
| 59 | + deprecation_message: #deprecation_message, |
| 60 | + display_message: #display_message, |
| 61 | + alias: #alias, |
| 62 | + display_icon: #display_icon, |
| 63 | + design: #design, |
| 64 | + throws_error: #throws_error, |
| 65 | + parameters: vec![#(#parameters),*], |
| 66 | + } |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + // `Function` carries no instance/state (dispatch always resolves |
| 71 | + // through `Base`), so unlike `runtime_function` this needs no |
| 72 | + // `manual` escape hatch — it's always safe to auto-register. |
| 73 | + #hercules::inventory::submit! { |
| 74 | + #hercules::Registration(|action| { |
| 75 | + action.register_function::<#ident>() |
| 76 | + .unwrap_or_else(|e| panic!("failed to register function {}: {e}", stringify!(#ident))); |
| 77 | + }) |
| 78 | + } |
| 79 | + }) |
| 80 | +} |
0 commit comments