1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Field, Type, parse_macro_input};
4
5#[proc_macro_derive(ConfigurableHandler)]
6pub fn derive_init_function(input: TokenStream) -> TokenStream {
7 use syn::{Data, DeriveInput, Fields, parse_macro_input};
8
9 let input = parse_macro_input!(input as DeriveInput);
11
12 let struct_name = &input.ident;
14
15 let field_count = match &input.data {
17 Data::Struct(data_struct) => match &data_struct.fields {
18 Fields::Named(fields_named) => fields_named.named.len(),
19 Fields::Unnamed(fields_unnamed) => fields_unnamed.unnamed.len(),
20 Fields::Unit => 0, },
22 _ => {
23 return TokenStream::from(
24 syn::Error::new_spanned(input, "ConfigurableHandler can only be derived for structs.")
25 .to_compile_error(),
26 );
27 }
28 };
29
30 if field_count != 1 {
32 return TokenStream::from(
33 syn::Error::new_spanned(
34 input,
35 "ConfigurableHandler can only be derived for structs with exactly one field.",
36 )
37 .to_compile_error(),
38 );
39 }
40
41 let mut inner_type = None;
42
43 if let Data::Struct(data_struct) = input.data {
44 if let Fields::Named(fields_named) = data_struct.fields {
45 for field in fields_named.named {
46 inner_type = find_config_inner_type(field);
47 }
48 }
49 }
50 let inner_type = inner_type.expect("The struct must contain a field with type Config<X>.");
51
52 let generated = quote! {
53 impl #struct_name {
54 pub fn init_handler(config: Config<#inner_type>) -> #struct_name {
55 #struct_name {
56 config
57 }
58 }
59
60 pub fn id() -> &'static str {
61 stringify!(#struct_name)
62 }
63
64 pub fn config_file_name() -> &'static str {
65 stringify!(#struct_name.json)
66 }
67
68 pub fn config(&self) -> &Config<#inner_type> {
69 &self.config
70 }
71
72 pub fn reload_config(&mut self, config: Config<#inner_type>) {
73 self.config = config;
74 }
75 }
76 };
77 TokenStream::from(generated)
78}
79
80fn find_config_inner_type(field: Field) -> Option<Type> {
81 use syn::{PathArguments, Type, TypePath};
82
83 if let Type::Path(TypePath { path, .. }) = &field.ty {
84 if let Some(segment) = path.segments.last() {
87 if segment.ident == "Config" {
89 if let PathArguments::AngleBracketed(type_argument) = &segment.arguments {
90 if let Some(syn::GenericArgument::Type(inner)) = type_argument.args.first() {
91 return Some(inner.clone());
92 }
93 }
94 }
95 }
96 }
97 None
98}