1#![feature(iterator_try_collect)]
2
3extern crate proc_macro;
4
5use proc_macro::TokenStream;
6
7use syn::*;
8use syn::punctuated::*;
9
10use quote::quote;
11
12use std::ffi::CString;
13
14struct FieldInfo {
15 ident : Ident,
16 ty : Type,
17 rename : Option<Expr>,
18 explain : Option<Expr>,
19}
20
21impl FieldInfo {
22 fn new(field : Field) -> Result<Option<Self>> {
23 let mut skip = false;
24 let mut rename = None;
25 let mut explain = None;
26 for attr in field.attrs {
27 if attr.path().is_ident("frei0r") {
28 let metas: Punctuated<Meta, Token![,]> = attr.parse_args_with(Punctuated::parse_terminated)?;
29 for meta in metas {
30 match meta {
31 Meta::Path(path) => {
32 let ident = path.require_ident()?;
33 match ident {
34 ident if ident == "skip" => {
35 if rename.is_some() || explain.is_some() {
36 return Err(Error::new_spanned(path, "skip attribute cannot be specified with other attribute"));
37 }
38
39 if skip {
40 return Err(Error::new_spanned(path, "attempting to set skip attribute more than once"));
41 } else {
42 skip = true;
43 }
44 },
45 _ => Err(Error::new_spanned(path, "unknown attribute"))?,
46 }
47 },
48 Meta::NameValue(meta_name_value) => {
49 let ident = meta_name_value.path.require_ident()?;
50 match ident {
51 ident if ident == "rename" => {
52 if skip {
53 return Err(Error::new_spanned(meta_name_value, "skip attribute cannot be specified with other attribute"));
54 }
55
56 rename = match rename {
57 Some(_) => Err(Error::new_spanned(meta_name_value, "attempting to set rename attribute more than once"))?,
58 None => Some(meta_name_value.value),
59 };
60 },
61 ident if ident == "explain" => {
62 if skip {
63 return Err(Error::new_spanned(meta_name_value, "skip attribute cannot be specified with other attribute"));
64 }
65
66 explain = match explain {
67 Some(_) => Err(Error::new_spanned(meta_name_value, "attempting to set explain attribute more than once"))?,
68 None => Some(meta_name_value.value),
69 };
70 },
71 _ => Err(Error::new_spanned(meta_name_value, "unknown attribute"))?,
72 }
73 },
74 Meta::List(meta_list) => Err(Error::new_spanned(meta_list, "unknown attribute"))?,
75 }
76 }
77 }
78 }
79
80 if skip {
81 return Ok(None);
82 }
83
84 Ok(Some(Self {
85 ident : field.ident.unwrap(),
86 ty : field.ty,
87 rename,
88 explain,
89 }))
90 }
91
92 fn param_name(&self) -> Expr {
93 self.rename.clone().unwrap_or_else(|| {
94 let ident = self.ident.to_string();
95 let ident = CString::new(ident).unwrap();
96 let ident = proc_macro2::Literal::c_string(&ident);
97 parse_quote! { #ident }
98 })
99 }
100
101 fn param_explain(&self) -> Expr {
102 self.explain.clone().unwrap_or_else(|| parse_quote! { c"" })
103 }
104}
105
106struct DeriveInputInfo {
107 ident : Ident,
108 generics : Generics,
109 fields : Vec<FieldInfo>,
110}
111
112impl DeriveInputInfo {
113 fn new(derive_input : DeriveInput) -> Result<Self> {
114 match derive_input {
115 DeriveInput { ident, generics, data : Data::Struct(DataStruct { fields : Fields::Named(fields), .. }), .. } => Ok(Self {
116 ident,
117 generics,
118 fields : fields.named.into_iter().flat_map(|f| FieldInfo::new(f).transpose()).try_collect()?,
119 }),
120 _ => Err(Error::new_spanned(derive_input, "Derive macro PluginBase is only supported on struct with named fields."))
121 }
122 }
123}
124
125#[proc_macro_derive(PluginBase, attributes(frei0r))]
127pub fn derive_plugin_base(input : TokenStream) -> TokenStream {
128 DeriveInputInfo::new(parse_macro_input!(input as DeriveInput))
129 .map(|info| {
130 let generics = info.generics;
131 let ident = &info.ident;
132
133 let param_count = info.fields.len();
134 let param_indices = (0..param_count).collect::<Vec<_>>();
135
136 let param_idents = info.fields.iter().map(|field| field.ident.clone()).collect::<Vec<_>>();
137 let param_tys = info.fields.iter().map(|field| field.ty .clone()).collect::<Vec<_>>();
138
139 let param_names = info.fields.iter().map(|field| field.param_name()) .collect::<Vec<_>>();
140 let param_explains = info.fields.iter().map(|field| field.param_explain()).collect::<Vec<_>>();
141
142 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
143 quote! {
144 unsafe impl #impl_generics ::frei0r_rs::PluginBase for #ident #ty_generics #where_clause {
145 fn param_count() -> usize {
146 #param_count
147 }
148
149 fn param_info(index : usize) -> ::frei0r_rs::ParamInfo {
150 match index {
151 #(#param_indices => ::frei0r_rs::ParamInfo {
152 name : #param_names,
153 kind : <#param_tys as ::frei0r_rs::Param>::kind(),
154 explanation : #param_explains,
155 }),*,
156 _ => unreachable!()
157 }
158 }
159
160 fn param_ref(&self, index : usize) -> ::frei0r_rs::ParamRef<'_> {
161 match index {
162 #(#param_indices => <#param_tys as ::frei0r_rs::Param>::as_ref(&self.#param_idents)),*,
163 _ => unreachable!()
164 }
165 }
166
167 fn param_mut(&mut self, index : usize) -> ::frei0r_rs::ParamMut<'_> {
168 match index {
169 #(#param_indices => <#param_tys as ::frei0r_rs::Param>::as_mut(&mut self.#param_idents)),*,
170 _ => unreachable!()
171 }
172 }
173 }
174 }
175 })
176 .unwrap_or_else(|err| err.into_compile_error())
177 .into()
178}
179