1#![warn(missing_docs)]
4#![allow(clippy::tabs_in_doc_comments)]
5
6extern crate proc_macro;
7
8mod dessin_macro;
9
10use proc_macro2::TokenStream;
11use quote::{__private::mk_ident, quote, spanned::Spanned};
12use syn::{parse_macro_input, DataStruct, DeriveInput, Fields, FieldsNamed, Type};
13
14#[proc_macro]
30pub fn dessin(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
31 let dessin = parse_macro_input!(tokens as dessin_macro::Dessin);
32
33 TokenStream::from(dessin).into()
34}
35
36#[proc_macro_derive(Shape, attributes(shape, local_transform))]
74pub fn shape(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
75 let input = parse_macro_input!(input as DeriveInput);
76 let name = input.ident;
77 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
78 let mut local_transform = None;
81
82 let fields = match input.data {
83 syn::Data::Struct(DataStruct {
84 fields: Fields::Named(FieldsNamed { named: fields, .. }),
85 ..
86 }) => fields
87 .into_iter()
88 .map(|field| {
89 let ident = field.ident.unwrap();
90 let ty = field.ty;
91
92 let mut skip = false;
93 let mut into = false;
94 let mut boolean = false;
95 let mut some = false;
96 let mut into_some = false;
97 let mut doc = None;
98 for attr in field.attrs {
99 if attr.path().is_ident("doc") {
100 doc = Some(attr);
101 continue;
102 }
103
104 if attr.path().is_ident("local_transform") {
105 if local_transform.is_some() {
106 panic!("Only one field can be a local_transform");
107 }
108
109 local_transform = Some(ident.clone());
110 skip = true;
111 }
112
113 if attr.path().is_ident("shape") {
114 attr.parse_nested_meta(|meta| {
115 if meta.path.is_ident("skip") {
116 skip = true;
117 }
118
119 if meta.path.is_ident("into") {
120 into = true;
121 }
122
123 if meta.path.is_ident("bool") {
124 boolean = true;
125 }
126
127 if meta.path.is_ident("some") {
128 some = true;
129 }
130
131 if meta.path.is_ident("into_some") {
132 into_some = true;
133 }
134
135 Ok(())
136 })
137 .unwrap()
138 }
139 }
140
141 if skip {
142 return quote!();
143 }
144
145 let with_ident = mk_ident(&format!("with_{ident}"), None);
146 if boolean {
147 quote!(
148 #doc
149 #[inline]
150 pub fn #ident(&mut self) -> &mut Self {
151 self.#ident = true;
152 self
153 }
154
155 #doc
156 #[inline]
157 pub fn #with_ident(mut self) -> Self {
158 self.#ident();
159 self
160 }
161 )
162 } else if into {
163 quote!(
164 #doc
165 #[inline]
166 pub fn #ident<__INTO__T: Into<#ty>>(&mut self, value: __INTO__T) -> &mut Self {
167 self.#ident = value.into();
168 self
169 }
170
171 #doc
172 #[inline]
173 pub fn #with_ident<__INTO__T: Into<#ty>>(mut self, value: __INTO__T) -> Self {
174 self.#ident(value);
175 self
176 }
177 )
178 } else if some {
179 let err_msg = syn::Error::new(ty.__span(), "Not supported").to_compile_error();
180 let Type::Path(syn::TypePath {
181 path: syn::Path { segments, .. },
182 ..
183 }) = ty
184 else {
185 return err_msg;
186 };
187
188 let ty = match segments.iter().next() {
189 Some(syn::PathSegment {
190 arguments:
191 syn::PathArguments::AngleBracketed(
192 syn::AngleBracketedGenericArguments { args, .. },
193 ),
194 ..
195 }) => match args.iter().next() {
196 Some(syn::GenericArgument::Type(t)) => t,
197 _ => return err_msg,
198 },
199 _ => return err_msg,
200 };
201
202 quote!(
203 #doc
204 #[inline]
205 pub fn #ident(&mut self, value: #ty) -> &mut Self {
206 self.#ident = Some(value);
207 self
208 }
209
210 #doc
211 #[inline]
212 pub fn #with_ident(mut self, value: #ty) -> Self {
213 self.#ident(value);
214 self
215 }
216 )
217 } else if into_some {
218 let err_msg = syn::Error::new(ty.__span(), "Not supported").to_compile_error();
219 let Type::Path(syn::TypePath {
220 path: syn::Path { segments, .. },
221 ..
222 }) = ty
223 else {
224 return err_msg;
225 };
226
227 let ty = match segments.iter().next() {
228 Some(syn::PathSegment {
229 arguments:
230 syn::PathArguments::AngleBracketed(
231 syn::AngleBracketedGenericArguments { args, .. },
232 ),
233 ..
234 }) => match args.iter().next() {
235 Some(syn::GenericArgument::Type(t)) => t,
236 _ => return err_msg,
237 },
238 _ => return err_msg,
239 };
240
241 quote!(
242 #doc
243 #[inline]
244 pub fn #ident<__INTO__T: Into<#ty>>(&mut self, value: __INTO__T) -> &mut Self {
245 self.#ident = Some(value.into());
246 self
247 }
248
249 #doc
250 #[inline]
251 pub fn #with_ident<__INTO__T: Into<#ty>>(mut self, value: __INTO__T) -> Self {
252 self.#ident(value);
253 self
254 }
255 )
256 } else {
257 quote!(
258 #doc
259 #[inline]
260 pub fn #ident(&mut self, value: #ty) -> &mut Self {
261 self.#ident = value;
262 self
263 }
264
265 #doc
266 #[inline]
267 pub fn #with_ident(mut self, value: #ty) -> Self {
268 self.#ident(value);
269 self
270 }
271 )
272 }
273 })
274 .collect::<Vec<_>>(),
275 syn::Data::Struct(_) => {
276 unreachable!()
277 }
278 syn::Data::Enum(_) => {
279 unreachable!()
280 }
281 syn::Data::Union(_) => {
282 unreachable!()
283 }
284 };
285
286 let shape_op_impl = if let Some(lt) = local_transform {
287 quote!(
288 impl #impl_generics ::dessin::prelude::ShapeOp for #name #ty_generics #where_clause {
289 #[inline]
290 fn transform(&mut self, transform_matrix: ::dessin::nalgebra::Transform2<f32>) -> &mut Self {
291 self.#lt = transform_matrix * self.#lt;
292 self
293 }
294
295 #[inline]
296 fn local_transform(&self) -> &::dessin::nalgebra::Transform2<f32> {
297 &self.#lt
298 }
299 }
300 )
301 } else {
302 quote!()
303 };
304
305 proc_macro::TokenStream::from(quote! {
306 impl #impl_generics #name #ty_generics #where_clause {
307 #(#fields)*
308 }
309
310 #shape_op_impl
311 })
312}