1use std::fmt::Display;
7use std::fmt::Formatter;
8use std::fmt::Result as FmtResult;
9use std::result;
10
11use proc_macro::LexError;
12use proc_macro::TokenStream;
13use proc_macro2::Ident;
14use proc_macro2::Span;
15use proc_macro2::TokenStream as Tokens;
16use quote::quote;
17use syn::Attribute;
18use syn::Binding;
19use syn::Data;
20use syn::DeriveInput;
21use syn::Fields;
22use syn::GenericParam;
23use syn::Generics;
24use syn::parenthesized;
25use syn::parse::Parse;
26use syn::parse::ParseStream;
27use syn::parse2;
28use syn::punctuated::Punctuated;
29use syn::token::Comma;
30use syn::token::Eq;
31use syn::Type;
32use syn::TypeGenerics;
33use syn::WhereClause;
34use syn::WherePredicate;
35
36
37type New = Option<()>;
39type Event = Option<Type>;
41type Message = Option<Type>;
43
44
45#[derive(Debug)]
47enum Error {
48 Error(String),
49 LexError(LexError),
50}
51
52impl Display for Error {
53 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
54 match *self {
55 Error::Error(ref e) => write!(f, "{e}"),
56 Error::LexError(ref e) => write!(f, "{e:?}"),
57 }
58 }
59}
60
61impl From<String> for Error {
62 fn from(string: String) -> Error {
63 Error::Error(string)
64 }
65}
66
67impl From<&'static str> for Error {
68 fn from(string: &'static str) -> Error {
69 Error::Error(string.to_string())
70 }
71}
72
73impl From<LexError> for Error {
74 fn from(error: LexError) -> Error {
75 Error::LexError(error)
76 }
77}
78
79type Result<T> = result::Result<T, Error>;
80
81
82#[proc_macro_derive(Widget, attributes(gui))]
130pub fn widget(input: TokenStream) -> TokenStream {
131 match expand_widget(input) {
132 Ok(tokens) => tokens,
133 Err(error) => panic!("{}", error),
134 }
135}
136
137fn expand_widget(input: TokenStream) -> Result<TokenStream> {
138 let input = parse2::<DeriveInput>(input.into()).map_err(|_| "unable to parse input")?;
139 let (new, event, message) = parse_attributes(&input.attrs)?;
140 let tokens = expand_widget_input(new, &event, &message, &input)?;
141 Ok(tokens.into())
142}
143
144fn parse_attributes(attributes: &[Attribute]) -> Result<(New, Event, Message)> {
146 let (new, event, message) = attributes.iter().map(parse_attribute).try_fold(
147 (None, None, None),
148 |(new1, event1, message1), result2| {
149 let (new2, event2, message2) = result2?;
150 Result::Ok((new2.or(new1), event2.or(event1), message2.or(message1)))
151 },
152 )?;
153
154 Ok((new, event, message))
157}
158
159fn parse_gui_attribute(item: Attr) -> Result<(New, Event, Message)> {
161 match item {
162 Attr::Ident(ref ident) if ident == "default_new" => {
163 Ok((Some(()), None, None))
164 },
165 Attr::Binding(binding) => {
166 if binding.ident == "Event" {
167 Ok((None, Some(binding.ty), None))
168 } else if binding.ident == "Message" {
169 Ok((None, None, Some(binding.ty)))
170 } else {
171 Err(Error::from("encountered unknown binding attribute"))
172 }
173 },
174 _ => Err(Error::from("encountered unknown attribute")),
175 }
176}
177
178fn parse_gui_attributes(list: AttrList) -> Result<(New, Event, Message)> {
180 let mut new = None;
181 let mut event = None;
182 let mut message = None;
183
184 for item in list.0 {
185 let (this_new, this_event, this_message) = parse_gui_attribute(item)?;
186 new = this_new.or(new);
187 event = this_event.or(event);
188 message = this_message.or(message);
189 }
190 Ok((new, event, message))
191}
192
193
194struct AttrList(Punctuated<Attr, Comma>);
196
197impl Parse for AttrList {
198 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
199 let content;
200 let _ = parenthesized!(content in input);
201 let list = content.parse_terminated(Attr::parse)?;
202
203 Ok(Self(list))
204 }
205}
206
207
208#[allow(clippy::large_enum_variant)]
209enum Attr {
210 Ident(Ident),
211 Binding(Binding),
212}
213
214impl Parse for Attr {
215 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
216 if input.peek2(Eq) {
219 let bind = input.parse::<Binding>()?;
220 Ok(Attr::Binding(bind))
221 } else {
222 input.parse::<Ident>().map(Attr::Ident)
223 }
224 }
225}
226
227
228fn parse_attribute(attribute: &Attribute) -> Result<(New, Event, Message)> {
230 if attribute.path.is_ident("gui") {
231 let tokens = attribute.tokens.clone();
232 let attr = parse2::<AttrList>(tokens).map_err(|err| {
233 format!("unable to parse attributes: {err:?}")
234 })?;
235
236 parse_gui_attributes(attr)
237 } else {
238 Ok((None, None, None))
239 }
240}
241
242fn expand_widget_input(
244 new: New,
245 event: &Event,
246 message: &Message,
247 input: &DeriveInput,
248) -> Result<Tokens> {
249 match input.data {
250 Data::Struct(ref data) => {
251 check_struct_fields(&data.fields)?;
252 Ok(expand_widget_traits(new, event, message, input))
253 },
254 _ => Err(Error::from("#[derive(Widget)] is only defined for structs")),
255 }
256}
257
258fn check_struct_fields(fields: &Fields) -> Result<()> {
264 let checks = [("id", "::gui::Id")];
265
266 for (req_field, req_type) in &checks {
267 let _ = fields
268 .iter()
269 .find(|field| {
270 if let Some(ref ident) = field.ident {
271 ident == req_field
272 } else {
273 false
274 }
275 })
276 .ok_or_else(|| Error::from(format!("struct field {req_field}: {req_type} not found")))?;
277 }
278 Ok(())
279}
280
281fn expand_widget_traits(new: New, event: &Event, message: &Message, input: &DeriveInput) -> Tokens {
283 let new_impl = expand_new_impl(new, input);
284 let renderable = expand_renderable_trait(input);
285 let object = expand_object_trait(input);
286 let widget = expand_widget_trait(event, message, input);
287
288 quote! {
289 #new_impl
290 #renderable
291 #object
292 #widget
293 }
294}
295
296
297fn expand_new_impl(new: New, input: &DeriveInput) -> Tokens {
299 let name = &input.ident;
300 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
301
302 match new {
303 Some(..) => {
304 quote! {
305 #[allow(dead_code)]
306 impl #impl_generics #name #ty_generics #where_clause {
307 pub fn new(id: ::gui::Id) -> Self {
308 #name {
309 id,
310 }
311 }
312 }
313 }
314 },
315 None => quote! {},
316 }
317}
318
319fn expand_renderable_trait(input: &DeriveInput) -> Tokens {
321 let name = &input.ident;
322 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
323
324 quote! {
325 impl #impl_generics ::gui::Renderable for #name #ty_generics #where_clause {
326 fn type_id(&self) -> ::std::any::TypeId {
327 ::std::any::TypeId::of::<#name #ty_generics>()
328 }
329
330 fn render(
331 &self,
332 cap: &dyn ::gui::Cap,
333 renderer: &dyn ::gui::Renderer,
334 bbox: ::gui::BBox,
335 ) -> ::gui::BBox {
336 renderer.render(self, cap, bbox)
337 }
338
339 fn render_done(
340 &self,
341 cap: &dyn ::gui::Cap,
342 renderer: &dyn ::gui::Renderer,
343 bbox: ::gui::BBox,
344 ) {
345 renderer.render_done(self, cap, bbox)
346 }
347 }
348 }
349}
350
351
352fn expand_object_trait(input: &DeriveInput) -> Tokens {
354 let name = &input.ident;
355 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
356
357 quote! {
358 impl #impl_generics ::gui::Object for #name #ty_generics #where_clause {
359 fn id(&self) -> ::gui::Id {
360 self.id
361 }
362 }
363 }
364}
365
366fn expand_widget_trait(event: &Event, message: &Message, input: &DeriveInput) -> Tokens {
368 let name = &input.ident;
369 let (generics, ty_generics, where_clause) = split_for_impl(&input.generics, event, message);
370
371 let event = if let Some(event) = event {
372 quote! { #event }
373 } else {
374 let ident = Ident::new("__E", Span::call_site());
375 quote! { #ident }
376 };
377
378 let message = if let Some(message) = message {
379 quote! { #message }
380 } else {
381 let ident = Ident::new("__M", Span::call_site());
382 quote! { #ident }
383 };
384
385 let widget = quote! { ::gui::Widget<#event, #message> };
386 quote! {
387 impl #generics #widget for #name #ty_generics #where_clause {
388 fn type_id(&self) -> ::std::any::TypeId {
389 ::std::any::TypeId::of::<#name #ty_generics>()
390 }
391 }
392 }
393}
394
395#[proc_macro_derive(Handleable, attributes(gui))]
417pub fn handleable(input: TokenStream) -> TokenStream {
418 match expand_handleable(input) {
419 Ok(tokens) => tokens,
420 Err(error) => panic!("{}", error),
421 }
422}
423
424fn expand_handleable(input: TokenStream) -> Result<TokenStream> {
425 let input = parse2::<DeriveInput>(input.into()).map_err(|_| "unable to parse input")?;
426 let (_, event, message) = parse_attributes(&input.attrs)?;
427 let tokens = expand_handleable_input(&event, &message, &input)?;
428 Ok(tokens.into())
429}
430
431fn expand_handleable_input(
433 event: &Event,
434 message: &Message,
435 input: &DeriveInput,
436) -> Result<Tokens> {
437 match input.data {
438 Data::Struct(_) => Ok(expand_handleable_trait(event, message, input)),
439 _ => Err(Error::from("#[derive(Handleable)] is only defined for structs")),
440 }
441}
442
443fn extend_generics(generics: &Generics, ident: Ident) -> Generics {
446 let param = GenericParam::Type(ident.into());
447 let mut generics = generics.clone();
448 generics.params.push(param);
449 generics
450}
451
452fn extend_where_clause(where_clause: &Option<WhereClause>, ident: &Ident) -> WhereClause {
454 if let Some(where_clause) = where_clause {
455 let predicate = quote! { #ident: 'static };
456 let predicate = parse2::<WherePredicate>(predicate).unwrap();
457 let mut where_clause = where_clause.clone();
458 where_clause.predicates.push(predicate);
459 where_clause
460 } else {
461 let where_clause = quote! { where #ident: 'static };
468 parse2::<WhereClause>(where_clause).unwrap()
469 }
470}
471
472fn split_for_impl<'g>(
476 generics: &'g Generics,
477 event: &Event,
478 message: &Message,
479) -> (Generics, TypeGenerics<'g>, Option<WhereClause>) {
480 let (_, ty_generics, _) = generics.split_for_impl();
481 let generics = generics.clone();
482 let where_clause = generics.where_clause.clone();
483
484 let (generics, where_clause) = if event.is_none() {
485 let ident = Ident::new("__E", Span::call_site());
486 let generics = extend_generics(&generics, ident.clone());
487 let where_clause = extend_where_clause(&where_clause, &ident);
488 (generics, Some(where_clause))
489 } else {
490 (generics, where_clause)
491 };
492
493 let (generics, where_clause) = if message.is_none() {
494 let ident = Ident::new("__M", Span::call_site());
495 let generics = extend_generics(&generics, ident.clone());
496 let where_clause = extend_where_clause(&where_clause, &ident);
497 (generics, Some(where_clause))
498 } else {
499 (generics, where_clause)
500 };
501
502 (generics, ty_generics, where_clause)
503}
504
505fn expand_handleable_trait(event: &Event, message: &Message, input: &DeriveInput) -> Tokens {
507 let name = &input.ident;
508 let (generics, ty_generics, where_clause) = split_for_impl(&input.generics, event, message);
509
510 let event = if let Some(event) = event {
511 quote! { #event }
512 } else {
513 let ident = Ident::new("__E", Span::call_site());
514 quote! { #ident }
515 };
516
517 let message = if let Some(message) = message {
518 quote! { #message }
519 } else {
520 let ident = Ident::new("__M", Span::call_site());
521 quote! { #ident }
522 };
523
524 let handleable = quote! { ::gui::Handleable<#event, #message> };
525 quote! {
526 impl #generics #handleable for #name #ty_generics #where_clause {}
527 }
528}
529
530
531#[cfg(test)]
532mod tests {
533 use super::*;
534
535 #[test]
536 fn default_widget_attributes() {
537 let tokens = quote! {
538 struct Bar { }
539 };
540
541 let input = parse2::<DeriveInput>(tokens).unwrap();
542 let (new, event, message) = parse_attributes(&input.attrs).unwrap();
543 assert_eq!(new, None);
544 assert_eq!(event, None);
545 assert_eq!(message, None);
546 }
547
548 #[test]
549 fn default_new() {
550 let tokens = quote! {
551 #[gui(default_new)]
552 struct Bar { }
553 };
554
555 let input = parse2::<DeriveInput>(tokens).unwrap();
556 let (new, event, message) = parse_attributes(&input.attrs).unwrap();
557 assert_eq!(new, Some(()));
558 assert_eq!(event, None);
559 assert_eq!(message, None);
560 }
561
562 #[test]
563 fn custom_event() {
564 let tokens = quote! {
565 #[gui(Event = FooBarBazEvent)]
566 struct Bar { }
567 };
568
569 let input = parse2::<DeriveInput>(tokens).unwrap();
570 let (new, event, message) = parse_attributes(&input.attrs).unwrap();
571 assert_eq!(new, None);
572 assert_eq!(message, None);
573
574 let tokens = quote! { FooBarBazEvent };
575 let foobar = parse2::<Type>(tokens).unwrap();
576 assert_eq!(event, Some(foobar));
577 }
578
579 #[test]
581 fn custom_message() {
582 let tokens = quote! {
583 #[gui(Message = SomeMessage)]
584 struct Foo { }
585 };
586
587 let input = parse2::<DeriveInput>(tokens).unwrap();
588 let (new, event, message) = parse_attributes(&input.attrs).unwrap();
589 assert_eq!(new, None);
590 assert_eq!(event, None);
591
592 let tokens = quote! { SomeMessage };
593 let some_message = parse2::<Type>(tokens).unwrap();
594 assert_eq!(message, Some(some_message));
595 }
596
597 #[test]
600 fn custom_event_and_message() {
601 let tokens = quote! {
602 #[gui(Event = FooBar, Message = FooBaz)]
603 struct Foo { }
604 };
605
606 let input = parse2::<DeriveInput>(tokens).unwrap();
607 let (new, event, message) = parse_attributes(&input.attrs).unwrap();
608 assert_eq!(new, None);
609
610 let tokens = quote! { FooBar };
611 let foobar = parse2::<Type>(tokens).unwrap();
612 assert_eq!(event, Some(foobar));
613
614 let tokens = quote! { FooBaz };
615 let foobaz = parse2::<Type>(tokens).unwrap();
616 assert_eq!(message, Some(foobaz));
617 }
618
619 #[test]
620 fn default_new_and_event_with_ignore() {
621 let tokens = quote! {
622 #[gui(default_new, Event = ())]
623 struct Baz { }
624 };
625
626 let input = parse2::<DeriveInput>(tokens).unwrap();
627 let (new, event, message) = parse_attributes(&input.attrs).unwrap();
628 assert_eq!(new, Some(()));
629 assert_eq!(message, None);
630
631 let tokens = quote! { () };
632 let parens = parse2::<Type>(tokens).unwrap();
633 assert_eq!(event, Some(parens));
634 }
635
636 #[test]
637 fn last_event_type_takes_precedence() {
638 let tokens = quote! {
639 #[gui(Event = Event1)]
640 #[gui(Event = Event2)]
641 struct Foo { }
642 };
643
644 let input = parse2::<DeriveInput>(tokens).unwrap();
645 let (_, event, _) = parse_attributes(&input.attrs).unwrap();
646
647 let tokens = quote! { Event2 };
648 let event2 = parse2::<Type>(tokens).unwrap();
649 assert_eq!(event, Some(event2));
650 }
651}