standard_plugin_macros/
lib.rs1use proc_macro::TokenStream;
12use quote::quote;
13use syn::spanned::Spanned as _;
14use syn::{Item, Type};
15
16#[proc_macro_attribute]
19pub fn ui(attr: TokenStream, item: TokenStream) -> TokenStream {
20 export(attr, item, World::Ui)
21}
22
23#[proc_macro_attribute]
27pub fn daemon(attr: TokenStream, item: TokenStream) -> TokenStream {
28 export(attr, item, World::Daemon)
29}
30
31enum World {
32 Ui,
33 Daemon,
34}
35
36fn export(attr: TokenStream, item: TokenStream, world: World) -> TokenStream {
37 if !attr.is_empty() {
38 let attr = proc_macro2::TokenStream::from(attr);
39 return syn::Error::new(attr.span(), "this attribute takes no arguments")
40 .to_compile_error()
41 .into();
42 }
43 let parsed = syn::parse_macro_input!(item as Item);
44 let ty: Type = match &parsed {
45 Item::Struct(item) => {
46 if !item.generics.params.is_empty() {
47 return generic_error(item.generics.span());
48 }
49 let ident = &item.ident;
50 syn::parse_quote!(#ident)
51 }
52 Item::Enum(item) => {
53 if !item.generics.params.is_empty() {
54 return generic_error(item.generics.span());
55 }
56 let ident = &item.ident;
57 syn::parse_quote!(#ident)
58 }
59 Item::Impl(item) if item.trait_.is_some() => {
60 if !item.generics.params.is_empty() {
61 return generic_error(item.generics.span());
62 }
63 (*item.self_ty).clone()
64 }
65 other => {
66 return syn::Error::new(
67 other.span(),
68 "put this attribute on the plugin's struct or enum, or on its plugin trait impl",
69 )
70 .to_compile_error()
71 .into();
72 }
73 };
74 let export = match world {
75 World::Ui => quote!(::standard_plugin::__export_ui!(#ty);),
76 World::Daemon => quote!(::standard_plugin::__export_daemon!(#ty);),
77 };
78 quote!(#parsed #export).into()
79}
80
81fn generic_error(span: proc_macro2::Span) -> TokenStream {
82 syn::Error::new(
83 span,
84 "a plugin type cannot be generic: the component exports one type",
85 )
86 .to_compile_error()
87 .into()
88}