zng_app_proc_macros/
wgt_property_attrs.rs
1use quote::ToTokens;
2use syn::{parse::Parse, *};
3
4use crate::util::crate_core;
5
6pub(crate) fn expand_easing(args: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
7 let data = parse_macro_input!(input as PropertyAttrData);
8 let args = parse_macro_input!(args as Args);
9 let PropertyAttrData {
10 builder,
11 is_unset: property_is_unset,
12 property,
13 is_when,
14 ..
15 } = &data;
16 let Args { duration, easing } = &args;
17
18 if *property_is_unset {
19 return quote! {
20 compile_error!{"cannot set `easing` in unset assign\n\n note: you can use `#[easing(unset)]` to unset in a normal assign to unset easing"}
21 #data
22 }.into();
23 }
24
25 let is_unset = args.is_unset();
26
27 let core = crate_core();
28 let name = "zng::core::widget::easing";
29
30 let property_ident = &property.segments.last().unwrap().ident;
31 let meta_ident = ident!("{property_ident}_");
32 let property_meta = if property.get_ident().is_some() {
33 quote! {
34 #builder.#meta_ident()
35 }
36 } else {
37 quote! {
38 #property::#meta_ident(#core::widget::base::WidgetImpl::base(#builder))
39 }
40 };
41
42 if *is_when {
43 if is_unset {
44 return quote! {
45 compile_error!{"cannot unset `easing` in when assign, try `#[easing(0.ms())]`"}
46 #data
47 }
48 .into();
49 }
50
51 return quote! {
52 {
53 let __data__ = #core::widget::easing_property::easing_when_data(
54 #property_meta.input_types(),
55 {
56 use #core::layout::unit::TimeUnits as _;
57 #duration
58 },
59 std::sync::Arc::new({
60 use #core::var::animation::easing::*;
61 #easing
62 }),
63 );
64 let id__ = #property_meta.id();
65 #core::widget::base::WidgetImpl::base(&mut *#builder).push_when_build_action_data__(
66 id__,
67 #name,
68 __data__,
69 );
70 }
71 #data
72 }
73 .into();
74 }
75
76 let r = if is_unset {
77 quote! {
78 #core::widget::easing_property::easing_property_unset(
79 #property_meta.input_types()
80 );
81 let id__ = #property_meta.id();
82 #core::widget::base::WidgetImpl::base(&mut *#builder).push_unset_property_build_action__(
83 id__,
84 #name,
85 );
86 #data
87 }
88 } else {
89 quote! {
90 {
91 let __actions__ = #core::widget::easing_property::easing_property(
92 #property_meta.input_types(),
93 {
94 use #core::layout::unit::TimeUnits as _;
95 #duration
96 },
97 std::sync::Arc::new({
98 use #core::var::animation::easing::*;
99 #easing
100 }),
101 );
102 if !__actions__.is_empty() {
103 let id__ = #property_meta.id();
104 #core::widget::base::WidgetImpl::base(&mut *#builder).push_property_build_action__(
105 id__,
106 #name,
107 __actions__
108 );
109 }
110 }
111 #data
112 }
113 };
114 r.into()
115}
116
117struct Args {
118 duration: Expr,
119 easing: Expr,
120}
121impl Args {
122 fn is_unset(&self) -> bool {
123 match &self.duration {
124 Expr::Path(p) => p.path.get_ident().map(|id| id == &ident!("unset")).unwrap_or(false),
125 _ => false,
126 }
127 }
128}
129impl Parse for Args {
130 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
131 Ok(Args {
132 duration: input.parse()?,
133 easing: {
134 if input.peek(Token![,]) {
135 let _ = input.parse::<Token![,]>();
136 input.parse()?
137 } else {
138 parse_quote!(linear)
139 }
140 },
141 })
142 }
143}
144
145pub(crate) struct PropertyAttrData {
146 pub pending_attrs: Vec<Attribute>,
150 pub data_ident: Ident,
152
153 pub builder: Ident,
155 pub is_unset: bool,
157 pub property: Path,
159 pub is_when: bool,
161}
162impl Parse for PropertyAttrData {
163 fn parse(input: parse::ParseStream) -> Result<Self> {
164 let item_mod: ItemMod = input.parse()?;
165
166 let mut builder = None;
167 let mut is_unset = false;
168 let mut property = None;
169 let mut is_when = false;
170
171 for item in item_mod.content.unwrap_or_else(|| non_user_error!("")).1 {
172 let mut f = match item {
173 Item::Fn(f) => f,
174 _ => non_user_error!(""),
175 };
176
177 let stmt = f.block.stmts.pop().unwrap_or_else(|| non_user_error!(""));
178 let expr = match stmt {
179 Stmt::Expr(e, _) => e,
180 _ => non_user_error!(""),
181 };
182
183 if f.sig.ident == ident!("builder_ident") {
184 let path = match expr {
185 Expr::Path(p) => p.path,
186 _ => non_user_error!(""),
187 };
188
189 let ident = match path.get_ident() {
190 Some(i) => i.clone(),
191 None => non_user_error!(""),
192 };
193
194 builder = Some(ident);
195 } else if f.sig.ident == ident!("property_path") {
196 let path = match expr {
197 Expr::Path(p) => p.path,
198 _ => non_user_error!(""),
199 };
200
201 property = Some(path);
202 } else if f.sig.ident == ident!("is_when") {
203 let lit = match expr {
204 Expr::Lit(l) => l,
205 _ => non_user_error!(""),
206 };
207
208 let lit_bool = match lit.lit {
209 Lit::Bool(b) => b,
210 _ => non_user_error!(""),
211 };
212
213 is_when = lit_bool.value();
214 } else if f.sig.ident == ident!("is_unset") {
215 let lit = match expr {
216 Expr::Lit(l) => l,
217 _ => non_user_error!(""),
218 };
219
220 let lit_bool = match lit.lit {
221 Lit::Bool(b) => b,
222 _ => non_user_error!(""),
223 };
224
225 is_unset = lit_bool.value();
226 } else {
227 non_user_error!("")
228 }
229 }
230
231 Ok(Self {
232 pending_attrs: item_mod.attrs,
233 data_ident: item_mod.ident,
234 builder: builder.unwrap_or_else(|| non_user_error!("")),
235 is_unset,
236 property: property.unwrap_or_else(|| non_user_error!("")),
237 is_when,
238 })
239 }
240}
241impl ToTokens for PropertyAttrData {
242 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
243 if self.pending_attrs.is_empty() {
244 return;
245 }
246
247 let span = self.pending_attrs[0].pound_token.span;
248
249 let Self {
250 pending_attrs,
251 data_ident,
252 builder,
253 is_unset,
254 property,
255 is_when,
256 } = self;
257
258 tokens.extend(quote_spanned! {span=>
259 #(#pending_attrs)*
260 mod #data_ident {
261 fn builder_ident() {
262 #builder
263 }
264
265 fn property_path() {
266 #property
267 }
268
269 fn is_unset() {
270 #is_unset
271 }
272
273 fn is_when() {
274 #is_when
275 }
276 }
277 })
278 }
279}