Skip to main content

dessin_macros/
lib.rs

1//! Macros for the [dessin](https://docs.rs/dessin/latest/dessin/) crate.
2
3#![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/// Entry point to build drawings
15/// ```ignore
16/// dessin!([
17/// 	*Text(
18/// 		text = "Hi",
19/// 		fill = Srgba::new(255, 0, 0, 255),
20/// 	),
21/// 	Line(
22/// 		from = [0., 10.],
23/// 		to = [10., 0.],
24/// 	),
25/// ] > *(
26/// 	translate = [-5., 5.],
27/// 	fill = Srgba::new(0, 255, 0, 255),
28/// ))
29#[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/// Auto implements setter for each members
37///
38/// ```rust
39/// # #[macro_use] extern crate dessin_macros;
40/// # use std::sync::{Arc, RwLock};
41///
42/// #[derive(Shape)]
43/// struct MyShape {
44/// 	// fn my_parameter(&mut self, v: u32)
45/// 	my_parameter: u32,
46///
47/// 	// fn my_bool(&mut self)
48/// 	// set my_bool to true if called
49/// 	#[shape(bool)]
50/// 	my_bool: bool,
51///
52/// 	// No fn generated
53/// 	#[shape(skip)]
54/// 	skip_this: Arc<RwLock<Vec<u8>>>,
55///
56/// 	// fn skip_option(&mut self, v: u32)
57/// 	// set skip_option to Some(v) if called
58/// 	#[shape(some)]
59/// 	skip_option: Option<u32>,
60/// 	// fn or_not(&mut self, v: Option<u32>)
61/// 	or_not: Option<u32>,
62///
63/// 	// fn into_string<V: Into<String>>(&mut self, v: V)
64/// 	#[shape(into)]
65/// 	into_string: String,
66///
67/// 	// fn maybe_into_string<V: Into<String>>(&mut self, v: V)
68/// 	// set maybe_into_string to Some(v.into()) if called
69/// 	#[shape(into_some)]
70/// 	maybe_into_string: Option<String>,
71/// }
72/// ```
73#[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 vis = input.vis;
79
80	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}