utils_plugs_proc/
lib.rs

1//! help
2
3extern 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}