1mod utils;
2pub(crate) use utils::*;
3
4use proc_macro::TokenStream;
5use proc_macro2::TokenStream as TokenStream2;
6use quote::quote;
7use syn::{Data, DeriveInput, Fields, parse_macro_input};
8
9#[proc_macro_derive(Component, attributes(root, state))]
11pub fn component(input: TokenStream) -> TokenStream {
12 let input = parse_macro_input!(input as DeriveInput);
13 let struct_name = &input.ident;
14
15 let (impl_generics, ty_generics, where_clause) =
16 input.generics.split_for_impl();
17
18 let mut maybe_root_ts: Option<TokenStream2> = None;
19 let mut maybe_state_ts: Option<TokenStream2> = None;
20
21 if let Data::Struct(data_struct) = input.data
22 && let Fields::Named(named_fields) = data_struct.fields
23 {
24 for field in named_fields.named.iter() {
25 for attr in &field.attrs {
26 if attr.path().is_ident("root") {
27 let field_name = &field.ident;
28
29 if maybe_root_ts.is_some() {
30 return error(
31 struct_name,
32 "Only one field can have the #[root] attribute.",
33 );
34 }
35
36 let expanded = quote! {
37 impl #impl_generics Component for #struct_name #ty_generics #where_clause {}
38
39 impl #impl_generics AsRef<::grapes::gtk::Widget> for #struct_name #ty_generics #where_clause {
40 fn as_ref(&self) -> &::grapes::gtk::Widget {
41 use ::grapes::gtk::prelude::Cast;
42 self.#field_name.as_ref()
43 }
44 }
45 };
46
47 maybe_root_ts = Some(expanded);
48 } else if attr.path().is_ident("state") {
49 let field_name = &field.ident;
50 let field_type = &field.ty;
51
52 if let Some(_) = maybe_state_ts {
53 return error(
54 attr.path(),
55 "Using the #[state] attribute twice",
56 );
57 }
58
59 let generic_type = match extract_generic(field_type) {
60 Some(t) => t,
61 None => {
62 return error(
63 struct_name,
64 "Can't extract T from State<T>",
65 );
66 }
67 };
68
69 let expanded = quote! {
70 impl #impl_generics ::grapes::Updateable for #struct_name #ty_generics #where_clause {
71 type Message = #generic_type;
72
73 fn update(&self, value: #generic_type) {
74 self.#field_name.set(value);
75 }
76 }
77 };
78
79 maybe_state_ts = Some(expanded);
80 }
81 }
82 }
83 }
84
85 match (maybe_root_ts, maybe_state_ts) {
86 (None, _) => error(
87 struct_name,
88 "One of the fields must have the #[root] attribute.",
89 ),
90 (Some(root_ts), None) => root_ts.into(),
91 (Some(root_ts), Some(state_ts)) => {
92 let mut ts = TokenStream2::new();
93 ts.extend(root_ts);
94 ts.extend(state_ts);
95 ts.into()
96 }
97 }
98}
99
100#[proc_macro_derive(WindowComponent, attributes(root))]
102pub fn window_component(input: TokenStream) -> TokenStream {
103 let input = parse_macro_input!(input as DeriveInput);
104 let struct_name = &input.ident;
105
106 let mut maybe_root_ts: Option<TokenStream2> = None;
107
108 if let Data::Struct(data_struct) = input.data
109 && let Fields::Named(named_fields) = data_struct.fields
110 {
111 for field in named_fields.named.iter() {
112 for attr in &field.attrs {
113 if attr.path().is_ident("root") {
114 let field_name = &field.ident;
115
116 if maybe_root_ts.is_some() {
117 return error(
118 struct_name,
119 "Only one field can have the #[root] attribute.",
120 );
121 }
122
123 let expanded = quote! {
124 impl WindowComponent for #struct_name {
125 fn present(&self) {
127 self.#field_name.present();
128 }
129
130 fn destroy(&self) {
132 self.#field_name.destroy();
133 }
134 }
135 };
136
137 maybe_root_ts = Some(expanded);
138 }
139 }
140 }
141 }
142
143 match maybe_root_ts {
144 None => error(
145 struct_name,
146 "One of the fields must have the #[root] attribute.",
147 ),
148 Some(root_ts) => root_ts.into(),
149 }
150}