plugin_runtime_codegen/
lib.rs1extern crate proc_macro;
17
18use darling::FromMeta;
19use proc_macro2::{Ident, Span, TokenStream};
20use quote::quote;
21use syn::spanned::Spanned;
22use syn::{parse_macro_input, AttributeArgs, Error, FnArg, ItemFn, Pat, Path, Type};
23
24#[derive(darling::FromMeta)]
25struct PluginManifestInput {
26 #[darling(rename = "for")]
27 core_name: String,
28}
29
30#[proc_macro_attribute]
31pub fn declare_plugin(
32 meta: proc_macro::TokenStream,
33 func: proc_macro::TokenStream,
34) -> proc_macro::TokenStream {
35 let meta = parse_macro_input!(meta as AttributeArgs);
36 let func = parse_macro_input!(func as ItemFn);
37 let input = match PluginManifestInput::from_list(&meta) {
38 Ok(input) => input,
39 Err(err) => {
40 return err.write_errors().into();
41 }
42 };
43
44 let output = match plugin_impl(input, func) {
45 Ok(ts) => ts,
46 Err(err) => err.to_compile_error(),
47 };
48 output.into()
49}
50
51fn plugin_impl(input: PluginManifestInput, func: ItemFn) -> syn::Result<TokenStream> {
52 let _core_name = &input.core_name; let func_name = &func.ident;
54 let maps_args = func
55 .decl
56 .inputs
57 .iter()
58 .map(|arg| {
59 Ok(match arg_to_dep(&arg)? {
60 Some(ident_name) => {
61 quote!(require_plugin!(deps, #ident_name))
62 }
63 None => quote!(&mut map),
64 })
65 })
66 .collect::<syn::Result<Vec<_>>>()?;
67
68 let output = quote! {
69 pub struct PluginManifestImpl;
70
71 impl ::plugin_runtime::PluginManifest for PluginManifestImpl {
72 fn init(deps: &mut ::plugin_runtime::PluginList) -> ::plugin_runtime::FeatureMap {
73 let mut map = FeatureMap::default();
74 #func_name(#(#maps_args),*);
75 map
76 }
77 }
78
79 #func
80 };
81 Ok(output.into())
82}
83
84#[allow(unused)]
85fn test_path(path: &Path, expects: &[&str]) -> bool {
86 let mut iter = expects.iter();
87 for segment in &path.segments {
88 dbg!(&segment.ident);
89 if let Some(expect) = iter.next() {
90 if expect != &segment.ident.to_string() {
91 return false;
92 }
93 } else {
94 return false;
95 }
96 }
97 return true;
98}
99
100fn is_feature_map(_path: &Path) -> bool {
101 true
103}
104
105fn is_option_feature_map(_path: &Path) -> bool {
106 false
107 }
128
129fn arg_to_dep(arg: &FnArg) -> syn::Result<Option<Ident>> {
130 match arg {
131 FnArg::Captured(arg_captured) => {
132 let ty = &arg_captured.ty;
133 let pat = &arg_captured.pat;
134
135 let (optional, mutable) = match ty {
136 Type::Reference(reference) => {
137 if reference.lifetime.is_some() {
138 return Err(Error::new(
139 ty.span(),
140 "Expected &FeatureMap or &mut FeatureMap, found lifetime",
141 ));
142 }
143 let optional = match reference.elem.as_ref() {
144 Type::Path(path) => {
145 if is_feature_map(&path.path) {
146 false
147 } else if is_option_feature_map(&path.path) {
148 true
149 } else {
150 return Err(Error::new(
151 ty.span(),
152 "Expected &[mut] FeatureMap, got unexpected type",
153 ));
154 }
155 }
156 _ => {
157 return Err(Error::new(
158 ty.span(),
159 "Expected &[mut] FeatureMap, got complex type",
160 ));
161 }
162 };
163 let mutable = reference.mutability.is_some();
164 (optional, mutable)
165 }
166 _ => {
167 return Err(Error::new(
168 ty.span(),
169 "Expected &FeatureMap or &mut FeatureMap, got non-reference",
170 ))
171 }
172 };
173
174 let ident = match pat {
175 Pat::Ident(ident) => &ident.ident,
176 _ => {
177 return Err(Error::new(pat.span(), "Expected a single argument name \"this\" or indicating dependency package name"))
178 }
179 };
180 let ident_name = ident.to_string();
181
182 if optional {
183 return Err(Error::new(
184 ty.span(),
185 "Option<FeatureMap> is not implemented yet",
186 ));
187 }
188 let arg = if "this" == &ident_name {
189 if !mutable {
190 return Err(Error::new(
191 ty.span(),
192 "\"this\" argument should be \"&mut FeatureMap\"",
193 ));
194 }
195 None
196 } else {
197 if mutable {
198 return Err(Error::new(
199 ty.span(),
200 "All arguments except \"this\" should be \"&FeatureMap\"",
201 ));
202 }
203 let ident_name = if ident_name.starts_with('_') {
204 &ident_name[1..]
205 } else {
206 &ident_name[..]
207 };
208 let ident_name = Ident::new(ident_name, Span::call_site());
209 Some(ident_name)
210 };
211 Ok(arg)
212 }
213 _ => Err(Error::new(arg.span(), "unexpected argument type")),
214 }
215}