1use proc_macro::TokenStream;
6use quote::quote;
7use syn::parse::Parse;
8use syn::punctuated::Punctuated;
9use syn::{
10 parse_macro_input, Attribute, Data, DeriveInput, Error as SynError, Path, Result as SynResult,
11 Token,
12};
13
14#[proc_macro_derive(Plugin, attributes(plugin))]
36pub fn derive_plugin(input: TokenStream) -> TokenStream {
37 let input = parse_macro_input!(input as DeriveInput);
38 match expand_plugin(input) {
39 Ok(tokens) => tokens.into(),
40 Err(err) => err.to_compile_error().into(),
41 }
42}
43
44fn expand_plugin(input: DeriveInput) -> SynResult<proc_macro2::TokenStream> {
45 match &input.data {
46 Data::Struct(_) => {}
47 Data::Enum(_) | Data::Union(_) => {
48 return Err(SynError::new_spanned(
49 &input.ident,
50 "#[derive(Plugin)] 仅支持结构体(struct)",
51 ));
52 }
53 }
54
55 let name = &input.ident;
56 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
57 let depends = parse_depends_attrs(&input.attrs)?;
58
59 let depend_type_ids = depends.iter().map(|path| {
60 quote! {
61 ::std::any::TypeId::of::<#path>()
62 }
63 });
64
65 Ok(quote! {
66 impl #impl_generics ::plugctx::Plugin for #name #ty_generics #where_clause {
67 fn dependencies(&self) -> ::std::vec::Vec<::std::any::TypeId> {
68 ::std::vec![
69 #(#depend_type_ids),*
70 ]
71 }
72
73 fn build(
74 &self,
75 ctx: &mut ::plugctx::Context,
76 ) -> ::std::result::Result<(), ::plugctx::Error> {
77 Self::on_build(self, ctx)
78 }
79 }
80 })
81}
82
83struct DependsList {
84 types: Punctuated<Path, Token![,]>,
85}
86
87impl Parse for DependsList {
88 fn parse(input: syn::parse::ParseStream) -> SynResult<Self> {
89 let content;
90 syn::parenthesized!(content in input);
91 let types = Punctuated::parse_terminated(&content)?;
92 Ok(Self { types })
93 }
94}
95
96fn parse_depends_attrs(attrs: &[Attribute]) -> SynResult<Vec<Path>> {
97 let mut deps = Vec::new();
98 for attr in attrs {
99 if !attr.path().is_ident("plugin") {
100 continue;
101 }
102 attr.parse_nested_meta(|meta| {
103 if meta.path.is_ident("depends") {
104 let list: DependsList = meta.input.parse()?;
105 deps.extend(list.types);
106 Ok(())
107 } else {
108 Err(meta.error("不支持的 #[plugin(...)] 键;仅支持 depends(...)"))
109 }
110 })?;
111 }
112 Ok(deps)
113}