1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{DeriveInput, LitStr, parse_macro_input};
4
5#[proc_macro_derive(ComponentPlugin, attributes(plugin))]
6pub fn derive_component_plugin(input: TokenStream) -> TokenStream {
7 derive_plugin_internal(
8 input,
9 quote!(::espforge_configuration::plugin::PluginKind::Component),
10 )
11}
12
13#[proc_macro_derive(DevicePlugin, attributes(plugin))]
14pub fn derive_device_plugin(input: TokenStream) -> TokenStream {
15 derive_plugin_internal(
16 input,
17 quote!(::espforge_configuration::plugin::PluginKind::Device),
18 )
19}
20
21fn derive_plugin_internal(input: TokenStream, kind_path: proc_macro2::TokenStream) -> TokenStream {
22 let input = parse_macro_input!(input as DeriveInput);
23 let name = &input.ident;
24 let mut required_features = quote!(vec![]);
25
26 let plugin_name = input
28 .attrs
29 .iter()
30 .filter(|a| a.path().is_ident("plugin"))
31 .find_map(|a| {
32 let mut name_val = None;
33 let _ = a.parse_nested_meta(|meta| {
34 if meta.path.is_ident("name") {
35 let value = meta.value()?;
36 let lit: LitStr = value.parse()?;
37 name_val = Some(lit.value());
38 }
39
40 if meta.path.is_ident("features") {
41 let value = meta.value()?;
42 let lit: LitStr = value.parse()?;
43 let feats: Vec<String> = lit
44 .value()
45 .split(',')
46 .map(|s| s.trim().to_string())
47 .filter(|s| !s.is_empty())
48 .collect();
49 required_features = quote!(vec![#(#feats.to_string()),*]);
50 }
51
52 Ok(())
53 });
54 name_val
55 })
56 .unwrap_or_else(|| name.to_string().trim_end_matches("Plugin").to_string());
57
58 let expanded = quote! {
59 impl ::espforge_configuration::plugin::Plugin for #name {
60 fn name(&self) -> &'static str {
61 #plugin_name
62 }
63
64 fn kind(&self) -> ::espforge_configuration::plugin::PluginKind {
65 #kind_path
66 }
67
68 fn validate(&self, properties: &::serde_yaml_ng::Value) -> ::anyhow::Result<()> {
69 self.validate_properties(properties)
70 }
71
72 fn dependencies(&self, properties: &::serde_yaml_ng::Value)
73 -> ::anyhow::Result<::std::vec::Vec<::espforge_configuration::plugin::Dependency>> {
74 self.resolve_dependencies(properties)
75 }
76
77 fn generate(&self, ctx: &::espforge_configuration::plugin::GenerationContext)
78 -> ::anyhow::Result<::espforge_configuration::plugin::GeneratedCode> {
79 self.generate_code(ctx)
80 }
81
82 fn required_features(&self) -> Vec<String> { #required_features }
83 }
84
85 ::inventory::submit! {
86 ::espforge_configuration::plugin::PluginRegistration(&#name)
87 }
88 };
89
90 TokenStream::from(expanded)
91}