1extern crate proc_macro;
4
5use proc_macro2::TokenStream;
6use quote::quote;
7
8use syn::{
9 parse_macro_input, Data, DeriveInput, Field, Fields, FieldsNamed, GenericArgument, Ident,
10 PathArguments, Type, TypePath,
11};
12
13const PLUGIN: &str = "Plugin";
14
15struct PluginField {
16 name: Ident,
17 generic: Type,
18}
19
20fn get_plugin_field(field: &Field) -> Option<PluginField> {
21 let generic = match &field.ty {
22 Type::Path(TypePath { path, .. }) => {
23 let ty = path.segments.first().unwrap();
24 let args = if ty.ident != PLUGIN {
25 return None;
26 } else {
27 &ty.arguments
28 };
29 if let PathArguments::AngleBracketed(generic) = args {
30 if let GenericArgument::Type(ty) = generic.args.first().unwrap() {
31 ty.to_owned()
32 } else {
33 return None;
34 }
35 } else {
36 return None;
37 }
38 }
39 _ => {
40 return None;
41 }
42 };
43
44 let name = field.ident.to_owned().unwrap();
45
46 let plugin_field = PluginField { name, generic };
47
48 Some(plugin_field)
49}
50
51fn concentric_impl(ast: DeriveInput) -> TokenStream {
52 let name = &ast.ident;
53
54 let fields = match &ast.data {
55 Data::Struct(v) => &v.fields,
56 _ => unimplemented!(),
57 };
58 let fields = match fields {
59 Fields::Named(FieldsNamed { ref named, .. }) => named,
60 _ => unimplemented!(),
61 };
62
63 let plugins: Vec<PluginField> = fields
64 .into_iter()
65 .filter_map(|f| get_plugin_field(f))
66 .collect();
67
68 let plugins: Vec<(Ident, Type)> = plugins.into_iter().map(|f| (f.name, f.generic)).collect();
69
70 let (plugin_names, plugin_generics): (Vec<Ident>, Vec<Type>) = plugins.into_iter().unzip();
71
72 quote! {
73
74 impl Concentric for #name {
75 fn concentric<T>(&mut self, _plugin: &Plugin<T>) -> &mut Self {
76 match std::any::type_name::<T>() {
77 #(
78 t if t.eq(std::any::type_name::<#plugin_generics>()) =>
79 self.#plugin_names.copy_from(_plugin),
80 )*
81 _ => {}
82 }
83 self
84 }
85 }
86
87 }
88}
89
90#[proc_macro_derive(Concentric)]
91pub fn concentric_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
92 let ast = parse_macro_input!(input as DeriveInput);
93
94 let output = concentric_impl(ast);
95
96 proc_macro::TokenStream::from(output)
97}