1use std::mem;
2
3use proc_macro2::{Ident, Span, TokenStream};
4use quote::{ToTokens, quote};
5use syn::{parse::Parse, punctuated::Punctuated, spanned::Spanned, *};
6
7use crate::util::{Attributes, Errors, crate_core, set_stream_span};
8
9pub fn expand(args: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
10 let mut errors = Errors::default();
11
12 let args = match parse::<Args>(args) {
13 Ok(a) => a,
14 Err(e) => {
15 errors.push_syn(e);
16 Args {
17 nest_group: parse_quote!(CONTEXT),
18 capture: false,
19 default: None,
20 impl_for: None,
21 }
22 }
23 };
24
25 let nest_group = args.nest_group;
26 let capture = args.capture;
27 let impl_for = args.impl_for;
28
29 let mut item = match parse::<ItemFn>(input.clone()) {
30 Ok(i) => i,
31 Err(e) => {
32 errors.push_syn(e);
33 let input = TokenStream::from(input);
34 let r = quote! {
35 #input
36 #errors
37 };
38 return r.into();
39 }
40 };
41 let mut attrs = Attributes::new(mem::take(&mut item.attrs));
42 let mut mtd_attrs = Attributes::new(vec![]);
43 mtd_attrs.docs.clone_from(&attrs.docs);
44 let mut extra_docs = quote!();
45
46 if capture {
48 attrs.tag_doc("c", "Capture-only property function");
49 mtd_attrs.tag_doc("c", "Capture-only property method");
50 if !attrs.docs.is_empty() {
51 extra_docs = quote! {
52 };
59 }
60
61 if item.sig.inputs.is_empty() {
62 errors.push(
63 "capture property functions must have at least 1 input: arg0, ..",
64 item.sig.inputs.span(),
65 );
66 }
67 } else {
68 attrs.tag_doc("P", "Property function");
69 mtd_attrs.tag_doc("P", "Property method");
70
71 if item.sig.inputs.len() < 2 {
72 errors.push(
73 "property functions must have at least 2 inputs: child: impl UiNode, arg0, ..",
74 item.sig.inputs.span(),
75 );
76 }
77 }
78
79 if item.sig.inputs.is_empty() {
80 let core = crate_core();
82 item.sig.inputs.push(parse_quote!(__child__: impl #core::widget::node::UiNode));
83 }
84
85 if let Some(async_) = &item.sig.asyncness {
86 errors.push("property functions cannot be `async`", async_.span());
87 }
88 if let Some(unsafe_) = &item.sig.unsafety {
89 errors.push("property functions cannot be `unsafe`", unsafe_.span());
90 }
91 if let Some(abi) = &item.sig.abi {
92 errors.push("property functions cannot be `extern`", abi.span());
93 }
94 if let Some(lifetime) = item.sig.generics.lifetimes().next() {
95 errors.push("property functions cannot declare lifetimes", lifetime.span());
96 }
97 if let Some(const_) = item.sig.generics.const_params().next() {
98 errors.push("property functions do not support `const` generics", const_.span());
99 }
100
101 let inputs: Vec<_> = item.sig.inputs.iter().map(|arg| Input::from_arg(arg, &mut errors)).collect();
102 if !inputs[0].ty.is_empty() && !capture {
103 if !matches!(inputs[0].kind, InputKind::UiNode) {
105 errors.push("first input must be `impl UiNode`", inputs[0].ty.span());
106 }
107 }
108
109 if capture {
110 let inputs = inputs.iter().map(|i| &i.ident);
111 if !item.block.stmts.is_empty() {
112 errors.push("capture property must have an empty body", item.block.span());
113 }
114 item.block.stmts.clear();
115 item.block.stmts.push(parse_quote! {
116 let _ = (#(#inputs,)*);
117 });
118 }
119 let item = item;
120 let first_input = if capture { 0 } else { 1 };
121
122 let output_span = match &item.sig.output {
123 ReturnType::Default => {
124 if !capture {
125 errors.push("output type must be `impl UiNode`", item.sig.ident.span());
126 }
127 proc_macro2::Span::call_site()
128 }
129 ReturnType::Type(_, ty) => {
130 if capture {
131 errors.push("capture must not have output", ty.span());
132 }
133 ty.span()
134 }
135 };
136
137 let extra = if errors.is_empty() {
141 let core = crate_core();
144 let cfg = &attrs.cfg;
145 let deprecated = &attrs.deprecated;
146 let vis = &item.vis;
147 let ident = &item.sig.ident;
148 let ident_str = ident.to_string();
149 let generics = &item.sig.generics;
150 let has_generics = !generics.params.empty_or_trailing();
151 let (impl_gens, ty_gens, where_gens) = generics.split_for_impl();
152 let path_gens = if has_generics { quote!(::#ty_gens) } else { quote!() };
153
154 let ident_unset = ident!("unset_{}", ident);
155 let ident_args = ident!("{}_args__", ident);
156 let ident_inputs = ident!("{}_inputs__", ident);
157 let ident_meta = ident!("{}_", ident);
158 let ident_sorted = ident!("{}__", ident);
159
160 let default;
161 let default_fn;
162 let args_default = match args.default {
163 Some(d) => Some((d.default.span(), d.args.to_token_stream())),
164 None => {
165 let mut default = quote!();
166 let first_input = if capture { 0 } else { 1 };
167 for input in &inputs[first_input..] {
168 match input.kind {
169 InputKind::Var => {
170 if ident_str.starts_with("is_") || ident_str.starts_with("has_") {
171 let core = set_stream_span(core.clone(), input.ty.span());
172 default.extend(quote_spanned! {input.ty.span()=>
173 #core::widget::builder::state_var(),
174 })
175 } else if ident_str.starts_with("get_") || ident_str.starts_with("actual_") {
176 let core = set_stream_span(core.clone(), input.ty.span());
177 default.extend(quote_spanned! {input.ty.span()=>
178 #core::widget::builder::getter_var(),
179 })
180 }
181 }
182 InputKind::UiNode => default.extend(quote! {
183 #core::widget::node::NilUiNode,
184 }),
185 InputKind::UiNodeList => default.extend(quote! {
186 #core::widget::node::UiVec::new(),
187 }),
188 InputKind::WidgetHandler if !has_generics => default.extend(quote! {
189 #core::handler::hn!(|_| {}),
190 }),
191 _ => {
192 default = quote!();
193 break;
194 }
195 }
196 }
197
198 if !default.is_empty() {
199 Some((Span::call_site(), default))
200 } else {
201 None
202 }
203 }
204 };
205
206 if let Some((span, args)) = args_default {
207 default = quote_spanned! {span=>
208 fn __default__ #impl_gens() -> std::boxed::Box<dyn #core::widget::builder::PropertyArgs> #where_gens {
209 #ident_meta {}.args(#args)
210 }
211 };
212 default_fn = quote! {
213 Some(__default__ #path_gens)
214 };
215 } else {
216 default = quote!();
217 default_fn = quote! {
218 None
219 };
220 }
221 let mut input_info = quote!();
222 let mut get_var = quote!();
223 let mut get_value = quote!();
224 let mut get_ui_node = quote!();
225 let mut get_ui_node_list = quote!();
226 let mut get_widget_handler = quote!();
227
228 let mut instantiate = quote!();
229 let mut input_idents = vec![];
230 let mut input_tys = vec![];
231 let mut storage_tys = vec![];
232 let mut input_to_storage = vec![];
233 let mut named_into = quote!();
234 let mut get_when_input = quote!();
235 let mut input_new_dyn = vec![];
236
237 let mut allowed_in_when_expr = true;
238 let mut allowed_in_when_assign = true;
239
240 for (i, input) in inputs[first_input..].iter().enumerate() {
241 let ident = &input.ident;
242 let input_ty = &input.ty;
243 input_idents.push(ident);
244 input_tys.push(input_ty);
245 storage_tys.push(&input.storage_ty);
246
247 let kind = input.kind;
248 let info_ty = &input.info_ty;
249 let storage_ty = &input.storage_ty;
250 input_info.extend(quote! {
251 #core::widget::builder::PropertyInput {
252 name: stringify!(#ident),
253 kind: #kind,
254 ty: std::any::TypeId::of::<#info_ty>(),
255 ty_name: std::any::type_name::<#info_ty>(),
256 },
257 });
258
259 match kind {
260 InputKind::Var => {
261 input_to_storage.push(quote! {
262 self.inputs().#ident(#ident)
263 });
264 get_var.extend(quote! {
265 #i => &self.#ident,
266 });
267 instantiate.extend(quote! {
268 std::clone::Clone::clone(&self.#ident),
269 });
270 named_into.extend(quote! {
271 pub fn #ident #impl_gens(&self, #ident: #input_ty) -> #storage_ty #where_gens {
272 #core::widget::builder::var_to_args(#ident)
273 }
274 });
275 input_new_dyn.push(quote! {
276 let __actions__ = #core::widget::builder::iter_input_build_actions(&__args__.build_actions, &__args__.build_actions_when_data, #i);
277 #core::widget::builder::new_dyn_var(&mut __inputs__, __actions__)
278 });
279 let get_ident = ident!("__w_{ident}__");
280 let get_ident_i = ident!("__w_{i}__");
281 get_when_input.extend(quote! {
282 pub fn #get_ident #impl_gens(&self)
283 -> (#core::widget::builder::WhenInputVar, impl #core::var::Var<#info_ty>) #where_gens {
284 #core::widget::builder::WhenInputVar::new::<#info_ty>()
285 }
286 pub fn #get_ident_i #impl_gens(&self)
287 -> (#core::widget::builder::WhenInputVar, impl #core::var::Var<#info_ty>) #where_gens {
288 #core::widget::builder::WhenInputVar::new::<#info_ty>()
289 }
290 });
291 }
292 InputKind::Value => {
293 allowed_in_when_assign = false;
294 input_to_storage.push(quote! {
295 self.inputs().#ident(#ident)
296 });
297 named_into.extend(quote! {
298 pub fn #ident #impl_gens(&self, #ident: #input_ty) -> #storage_ty #where_gens {
299 #core::widget::builder::value_to_args(#ident)
300 }
301 });
302 get_value.extend(quote! {
303 #i => &self.#ident,
304 });
305 instantiate.extend(quote! {
306 std::clone::Clone::clone(&self.#ident),
307 });
308 input_new_dyn.push(quote! {
309 let __actions__ = #core::widget::builder::iter_input_build_actions(&__args__.build_actions, &__args__.build_actions_when_data, #i);
310 #core::widget::builder::new_dyn_other(&mut __inputs__, __actions__)
311 });
312 let get_ident = ident!("__w_{ident}__");
313 let get_ident_i = ident!("__w_{i}__");
314 get_when_input.extend(quote! {
315 pub fn #get_ident #impl_gens(&self)
316 -> (#core::widget::builder::WhenInputVar, impl #core::var::Var<#info_ty>) #where_gens {
317 #core::widget::builder::WhenInputVar::new::<#info_ty>()
318 }
319 pub fn #get_ident_i #impl_gens(&self)
320 -> (#core::widget::builder::WhenInputVar, impl #core::var::Var<#info_ty>) #where_gens {
321 #core::widget::builder::WhenInputVar::new::<#info_ty>()
322 }
323 });
324 }
325 InputKind::UiNode => {
326 allowed_in_when_expr = false;
327 input_to_storage.push(quote! {
328 #core::widget::builder::ui_node_to_args(#ident)
329 });
330 named_into.extend(quote! {
331 pub fn #ident(&self, #ident: #input_ty) -> #core::widget::node::BoxedUiNode {
332 #core::widget::node::UiNode::boxed(#ident)
333 }
334 });
335 get_ui_node.extend(quote! {
336 #i => &self.#ident,
337 });
338 instantiate.extend(quote! {
339 self.#ident.take_on_init(),
340 });
341 input_new_dyn.push(quote! {
342 let __actions__ = #core::widget::builder::iter_input_build_actions(&__args__.build_actions, &__args__.build_actions_when_data, #i);
343 #core::widget::builder::new_dyn_ui_node(&mut __inputs__, __actions__)
344 });
345 let get_ident = ident!("__w_{ident}__");
346 let get_ident_i = ident!("__w_{i}__");
347 get_when_input.extend(quote! {
348 pub fn #get_ident(&self)
349 -> (#core::widget::builder::WhenInputVar, impl #core::var::Var<#core::widget::builder::UiNodeInWhenExprError>) {
350 #core::widget::builder::WhenInputVar::new::<#core::widget::builder::UiNodeInWhenExprError>()
351 }
352 pub fn #get_ident_i(&self)
353 -> (#core::widget::builder::WhenInputVar, impl #core::var::Var<#core::widget::builder::UiNodeInWhenExprError>) {
354 #core::widget::builder::WhenInputVar::new::<#core::widget::builder::UiNodeInWhenExprError>()
355 }
356 });
357 }
358 InputKind::UiNodeList => {
359 allowed_in_when_expr = false;
360 input_to_storage.push(quote! {
361 #core::widget::builder::ui_node_list_to_args(#ident)
362 });
363 named_into.extend(quote! {
364 pub fn #ident(&self, #ident: #input_ty) -> #core::widget::node::BoxedUiNodeList {
365 #core::widget::node::UiNodeList::boxed(#ident)
366 }
367 });
368 get_ui_node_list.extend(quote! {
369 #i => &self.#ident,
370 });
371 instantiate.extend(quote! {
372 self.#ident.take_on_init(),
373 });
374 input_new_dyn.push(quote! {
375 let __actions__ = #core::widget::builder::iter_input_build_actions(&__args__.build_actions, &__args__.build_actions_when_data, #i);
376 #core::widget::builder::new_dyn_ui_node_list(&mut __inputs__, __actions__)
377 });
378 let get_ident = ident!("__w_{ident}__");
379 let get_ident_i = ident!("__w_{i}__");
380 get_when_input.extend(quote! {
381 pub fn #get_ident(&self)
382 -> (#core::widget::builder::WhenInputVar, impl #core::var::Var<#core::widget::builder::UiNodeListInWhenExprError>) {
383 #core::widget::builder::WhenInputVar::new::<#core::widget::builder::UiNodeListInWhenExprError>()
384 }
385 pub fn #get_ident_i(&self)
386 -> (#core::widget::builder::WhenInputVar, impl #core::var::Var<#core::widget::builder::UiNodeListInWhenExprError>) {
387 #core::widget::builder::WhenInputVar::new::<#core::widget::builder::UiNodeListInWhenExprError>()
388 }
389 });
390 }
391 InputKind::WidgetHandler => {
392 allowed_in_when_expr = false;
393 input_to_storage.push(quote! {
394 #core::widget::builder::widget_handler_to_args(#ident)
395 });
396 named_into.extend(quote! {
397 pub fn #ident #impl_gens(&self, #ident: #input_ty) -> #input_ty #where_gens {
398 #ident
399 }
400 });
401 get_widget_handler.extend(quote! {
402 #i => &self.#ident,
403 });
404 instantiate.extend(quote! {
405 std::clone::Clone::clone(&self.#ident),
406 });
407 input_new_dyn.push(quote! {
408 let __actions__ = #core::widget::builder::iter_input_build_actions(&__args__.build_actions, &__args__.build_actions_when_data, #i);
409 #core::widget::builder::new_dyn_widget_handler(&mut __inputs__, __actions__)
410 });
411 let get_ident = ident!("__w_{ident}__");
412 let get_ident_i = ident!("__w_{i}__");
413 get_when_input.extend(quote! {
414 pub fn #get_ident #impl_gens(&self)
415 -> (#core::widget::builder::WhenInputVar, impl #core::var::Var<#core::widget::builder::WidgetHandlerInWhenExprError>) #where_gens {
416 #core::widget::builder::WhenInputVar::new::<#core::widget::builder::WidgetHandlerInWhenExprError>()
417 }
418 pub fn #get_ident_i #impl_gens(&self)
419 -> (#core::widget::builder::WhenInputVar, impl #core::var::Var<#core::widget::builder::WidgetHandlerInWhenExprError>) #where_gens {
420 #core::widget::builder::WhenInputVar::new::<#core::widget::builder::WidgetHandlerInWhenExprError>()
421 }
422 });
423 }
424 }
425 }
426
427 if !get_var.is_empty() {
428 get_var = quote! {
429 fn var(&self, __index__: usize) -> &dyn #core::var::AnyVar {
430 match __index__ {
431 #get_var
432 n => #core::widget::builder::panic_input(&self.property(), n, #core::widget::builder::InputKind::Var),
433 }
434 }
435 }
436 }
437 if !get_value.is_empty() {
438 get_value = quote! {
439 fn value(&self, __index__: usize) -> &dyn #core::var::AnyVarValue {
440 match __index__ {
441 #get_value
442 n => #core::widget::builder::panic_input(&self.property(), n, #core::widget::builder::InputKind::Value),
443 }
444 }
445 }
446 }
447 if !get_ui_node.is_empty() {
448 get_ui_node = quote! {
449 fn ui_node(&self, __index__: usize) -> &#core::widget::node::ArcNode<#core::widget::node::BoxedUiNode> {
450 match __index__ {
451 #get_ui_node
452 n => #core::widget::builder::panic_input(&self.property(), n, #core::widget::builder::InputKind::UiNode),
453 }
454 }
455 }
456 }
457 if !get_ui_node_list.is_empty() {
458 get_ui_node_list = quote! {
459 fn ui_node_list(&self, __index__: usize) -> &#core::widget::node::ArcNodeList<#core::widget::node::BoxedUiNodeList> {
460 match __index__ {
461 #get_ui_node_list
462 n => #core::widget::builder::panic_input(&self.property(), n, #core::widget::builder::InputKind::UiNodeList),
463 }
464 }
465 }
466 }
467 if !get_widget_handler.is_empty() {
468 get_widget_handler = quote! {
469 fn widget_handler(&self, __index__: usize) -> &dyn #core::widget::builder::AnyArcWidgetHandler {
470 match __index__ {
471 #get_widget_handler
472 n => #core::widget::builder::panic_input(&self.property(), n, #core::widget::builder::InputKind::WidgetHandler),
473 }
474 }
475 }
476 }
477
478 let mut sorted_inputs: Vec<_> = inputs[first_input..].iter().collect();
479 sorted_inputs.sort_by_key(|i| &i.ident);
480 let sorted_idents: Vec<_> = sorted_inputs.iter().map(|i| &i.ident).collect();
481 let sorted_tys: Vec<_> = sorted_inputs.iter().map(|i| &i.ty).collect();
482
483 let node_instance = ident_spanned!(output_span=> "__node__");
484
485 let docs = &attrs.docs;
486
487 let allowed_in_when_expr = if allowed_in_when_expr {
488 quote! {
489 pub const fn allowed_in_when_expr(&self) {}
490 }
491 } else {
492 quote!()
493 };
494 let allowed_in_when_assign = if allowed_in_when_assign {
495 quote! {
496 pub const fn allowed_in_when_assign(&self) {}
497 }
498 } else {
499 quote!()
500 };
501
502 let source_location = crate::widget_util::source_location(&core, ident.span());
503
504 let meta = quote! {
505 #cfg
506 #[doc(hidden)]
507 #[allow(non_camel_case_types)]
508 #vis struct #ident_meta { }
509 #cfg
510 #[doc(hidden)]
511 #[allow(dead_code)]
512 impl #ident_meta {
513 pub fn id(&self) -> #core::widget::builder::PropertyId {
514 #core::static_id! {
515 static ref ID: #core::widget::builder::PropertyId;
516 }
517 *ID
518 }
519
520 #allowed_in_when_expr
521 #allowed_in_when_assign
522
523 pub fn info #impl_gens(&self) -> #core::widget::builder::PropertyInfo #where_gens {
524 #core::widget::builder::PropertyInfo {
525 group: {
526 use #core::widget::builder::nest_group_items::*;
527 #nest_group
528 },
529 capture: #capture,
530 id: self.id(),
531 name: std::stringify!(#ident),
532 location: #source_location,
533 default: self.default_fn #path_gens(),
534 new: Self::args_dyn #path_gens,
535 inputs: std::boxed::Box::new([
536 #input_info
537 ]),
538 }
539 }
540
541 #vis const fn input_types #impl_gens(&self) -> #core::widget::builder::PropertyInputTypes<(#(#storage_tys,)*)> #where_gens {
542 #core::widget::builder::PropertyInputTypes::unit()
543 }
544
545 pub fn default_fn #impl_gens(&self) -> std::option::Option<fn () -> std::boxed::Box<dyn #core::widget::builder::PropertyArgs>> #where_gens {
546 #default
547 #default_fn
548 }
549
550 #vis fn args #impl_gens(
551 &self,
552 #(#input_idents: #input_tys),*
553 ) -> std::boxed::Box<dyn #core::widget::builder::PropertyArgs> #where_gens {
554 std::boxed::Box::new(#ident_args {
555 #(#input_idents: #input_to_storage),*
556 })
557 }
558
559 #vis fn args_sorted #impl_gens(
560 &self,
561 #(#sorted_idents: #sorted_tys),*
562 ) -> std::boxed::Box<dyn #core::widget::builder::PropertyArgs> #where_gens {
563 self.args(#(#input_idents),*)
564 }
565
566 fn args_dyn #impl_gens(
567 __args__: #core::widget::builder::PropertyNewArgs,
568 ) -> std::boxed::Box<dyn #core::widget::builder::PropertyArgs> #where_gens {
569 let mut __inputs__ = __args__.args.into_iter();
570 Box::new(#ident_args #path_gens {
571 #(#input_idents: { #input_new_dyn },)*
572 })
573 }
574
575 pub fn inputs(&self) -> #ident_inputs {
576 #ident_inputs { }
577 }
578 }
579 };
580 let instantiate = if capture {
581 quote! {
582 #[allow(unused)]
583 use self::#ident;
584 __child__
585 }
586 } else {
587 quote! {
588 let #node_instance = #ident(__child__, #instantiate);
589 #core::widget::node::UiNode::boxed(#node_instance)
590 }
591 };
592
593 let allow_deprecated = deprecated.as_ref().map(|_| {
594 quote! {
595 #[allow(deprecated)]
596 }
597 });
598
599 let args = quote! {
600 #cfg
601 #[doc(hidden)]
602 #[derive(std::clone::Clone)]
603 #[allow(non_camel_case_types)]
604 #vis struct #ident_args #impl_gens #where_gens {
605 #(#input_idents: #storage_tys),*
606 }
607 #cfg
608 #[doc(hidden)]
609 impl #impl_gens #core::widget::builder::PropertyArgs for #ident_args #ty_gens #where_gens {
610 fn clone_boxed(&self) -> std::boxed::Box<dyn #core::widget::builder::PropertyArgs> {
611 Box::new(std::clone::Clone::clone(self))
612 }
613
614 fn property(&self) -> #core::widget::builder::PropertyInfo {
615 #ident_meta { }.info #path_gens()
616 }
617
618 #allow_deprecated
619 fn instantiate(&self, __child__: #core::widget::node::BoxedUiNode) -> #core::widget::node::BoxedUiNode {
620 #instantiate
621 }
622
623 #get_var
624 #get_value
625 #get_ui_node
626 #get_ui_node_list
627 #get_widget_handler
628 }
629 };
630 let inputs = quote! {
631 #cfg
632 #[doc(hidden)]
633 #[allow(non_camel_case_types)]
634 #vis struct #ident_inputs { }
635 #cfg
636 #[doc(hidden)]
637 impl #ident_inputs {
638 #named_into
639 #get_when_input
640 }
641 };
642
643 let direct_impl = if let Some(impl_for) = impl_for {
644 let mut target = impl_for.target;
645 let (generics_impl, generics) = match &target.segments.last().unwrap().arguments {
646 PathArguments::None => (quote!(), quote!()),
647 PathArguments::AngleBracketed(b) => {
648 if b.args.is_empty() {
649 (quote!(), quote!())
650 } else if b.args.len() > 1 {
651 errors.push("only `<P>` generics is allowed", b.span());
652 (quote!(), quote!())
653 } else {
654 target.segments.last_mut().unwrap().arguments = PathArguments::None;
655 (quote!(<P: #core::widget::base::WidgetImpl>), quote!(<P>))
656 }
657 }
658 PathArguments::Parenthesized(p) => {
659 errors.push("only `<P>` generics is allowed", p.span());
660 (quote!(), quote!())
661 }
662 };
663 let docs = &mtd_attrs.docs;
664 quote! {
665 #cfg
666 impl #generics_impl #target #generics {
667 #(#docs)*
668 #deprecated
669 #vis fn #ident #impl_gens(&self, #(#input_idents: #input_tys),*) #where_gens {
670 let args = #ident_meta { }.args(#(#input_idents),*);
671 #core::widget::base::WidgetImpl::base_ref(self).mtd_property__(args)
672 }
673
674 #[doc(hidden)]
675 #[allow(dead_code)]
676 #vis fn #ident_unset(&self) {
677 #core::widget::base::WidgetImpl::base_ref(self).mtd_property_unset__(#ident_meta { }.id())
678 }
679
680 #[doc(hidden)]
681 #[allow(dead_code)]
682 #vis fn #ident_sorted #impl_gens(&mut self, #(#sorted_idents: #sorted_tys),*) #where_gens {
683 let args = #ident_meta { }.args_sorted(#(#sorted_idents),*);
684 #core::widget::base::WidgetImpl::base_ref(self).mtd_property__(args)
685 }
686
687 #[doc(hidden)]
688 #[allow(dead_code)]
689 #vis fn #ident_meta(&self) -> #ident_meta {
690 #ident_meta { }
691 }
692 }
693 }
694 } else {
695 quote!()
696 };
697
698 quote! {
699 #direct_impl
700
701 #cfg
702 #[doc(hidden)]
703 #[allow(non_camel_case_types)]
704 #vis trait #ident: #core::widget::base::WidgetExt {
705 type MetaType;
706
707 #(#docs)*
708 #deprecated
709 #[allow(clippy::too_many_arguments)]
710 fn #ident #impl_gens(&mut self, #(#input_idents: #input_tys),*) #where_gens {
711 let args = #ident_meta { }.args(#(#input_idents),*);
712 self.ext_property__(args)
713 }
714
715 fn #ident_unset(&mut self) {
717 self.ext_property_unset__(#ident_meta {}.id())
718 }
719
720 #[doc(hidden)]
721 fn #ident_sorted #impl_gens(&mut self, #(#sorted_idents: #sorted_tys),*) #where_gens {
722 let args = #ident_meta { }.args_sorted(#(#sorted_idents),*);
723 self.ext_property__(args)
724 }
725
726 #[doc(hidden)]
727 fn #ident_meta(&self) -> #ident_meta {
728 #ident_meta { }
729 }
730 }
731 #cfg
732 #[doc(hidden)]
733 impl self::#ident for #core::widget::base::WidgetBase {
734 type MetaType = ();
735 }
736 #cfg
737 #[doc(hidden)]
738 impl self::#ident for #core::widget::base::NonWidgetBase {
739 type MetaType = ();
740 }
741 #cfg
742 #[doc(hidden)]
743 impl self::#ident for #core::widget::builder::WgtInfo {
744 type MetaType = #ident_meta;
745 }
746
747 #meta
748 #args
749 #inputs
750 }
751 } else {
752 quote!()
753 };
754
755 let r = quote! {
756 #attrs
757 #extra_docs
758 #item
759 #extra
760 #errors
761 };
762 r.into()
763}
764
765struct Args {
766 nest_group: Expr,
767 capture: bool,
768 default: Option<Default>,
769 impl_for: Option<ImplFor>,
770}
771impl Parse for Args {
772 fn parse(input: parse::ParseStream) -> Result<Self> {
773 Ok(Args {
774 nest_group: input.parse()?,
775 capture: if input.peek(Token![,]) && input.peek2(keyword::capture) {
776 let _: Token![,] = input.parse()?;
777 let _: keyword::capture = input.parse()?;
778 true
779 } else {
780 false
781 },
782 default: if input.peek(Token![,]) && input.peek2(Token![default]) {
783 Some(input.parse()?)
784 } else {
785 None
786 },
787 impl_for: if input.peek(Token![,]) && (input.peek2(keyword::widget_impl) || input.peek2(Token![for])) {
788 Some(input.parse()?)
789 } else {
790 None
791 },
792 })
793 }
794}
795
796struct Default {
797 default: Token![default],
798 args: Punctuated<Expr, Token![,]>,
799}
800impl Parse for Default {
801 fn parse(input: parse::ParseStream) -> Result<Self> {
802 let _: Token![,] = input.parse()?;
803 let default = input.parse()?;
804 let inner;
805 parenthesized!(inner in input);
806 Ok(Default {
807 default,
808 args: Punctuated::parse_terminated(&inner)?,
809 })
810 }
811}
812
813struct ImplFor {
814 target: Path,
815}
816impl Parse for ImplFor {
817 fn parse(input: parse::ParseStream) -> Result<Self> {
818 let _: Token![,] = input.parse()?;
819 let _: keyword::widget_impl = input.parse()?;
820 let inner;
821 parenthesized!(inner in input);
822
823 Ok(ImplFor { target: inner.parse()? })
824 }
825}
826
827#[derive(Clone, Copy)]
828enum InputKind {
829 Var,
830 Value,
831 UiNode,
832 WidgetHandler,
833 UiNodeList,
834}
835impl ToTokens for InputKind {
836 fn to_tokens(&self, tokens: &mut TokenStream) {
837 let kin = match self {
838 InputKind::Var => ident!("Var"),
839 InputKind::Value => ident!("Value"),
840 InputKind::UiNode => ident!("UiNode"),
841 InputKind::WidgetHandler => ident!("WidgetHandler"),
842 InputKind::UiNodeList => ident!("UiNodeList"),
843 };
844 let core = crate_core();
845 tokens.extend(quote! {
846 #core::widget::builder::InputKind::#kin
847 });
848 }
849}
850
851struct Input {
852 ident: Ident,
853 kind: InputKind,
854 ty: TokenStream,
855 info_ty: TokenStream,
856 storage_ty: TokenStream,
857}
858impl Input {
859 fn from_arg(arg: &FnArg, errors: &mut Errors) -> Input {
860 let mut input = Input {
861 ident: ident!("__invalid__"),
862 kind: InputKind::Value,
863 ty: quote!(),
864 storage_ty: quote!(),
865 info_ty: quote!(),
866 };
867 match arg {
868 FnArg::Receiver(rcv) => {
869 errors.push("methods cannot be properties", rcv.span());
870 }
871 FnArg::Typed(t) => {
872 if !t.attrs.is_empty() {
873 errors.push("property input cannot have attributes", t.attrs[0].span());
874 }
875
876 match *t.pat.clone() {
877 Pat::Ident(id) => {
878 if id.ident == "self" {
879 errors.push("methods cannot be properties", id.ident.span());
880 }
881 input.ident = id.ident;
882 }
883 p => {
884 errors.push("property input can only have a simple ident", p.span());
885 }
886 }
887 let core = crate_core();
888
889 match *t.ty.clone() {
890 Type::ImplTrait(mut it) if it.bounds.len() == 1 => {
891 let bounds = it.bounds.pop().unwrap().into_value();
892 match bounds {
893 TypeParamBound::Trait(tra) if tra.lifetimes.is_none() && tra.paren_token.is_none() => {
894 let path = tra.path;
895 let seg = path.segments.last().unwrap();
896
897 fn ty_from_generic(
898 input: &mut Input,
899 errors: &mut Errors,
900 t: &Type,
901 kind: InputKind,
902 args: &PathArguments,
903 ) -> bool {
904 if let PathArguments::AngleBracketed(it) = args {
905 if it.args.len() == 1 {
906 input.kind = kind;
907 input.ty = t.to_token_stream();
908 input.info_ty = it.args.last().unwrap().to_token_stream();
909 return true;
910 }
911 }
912 errors.push("expected single generic param", args.span());
913 false
914 }
915
916 match seg.ident.to_string().as_str() {
917 "IntoVar" if !seg.arguments.is_empty() => {
918 if ty_from_generic(&mut input, errors, &t.ty, InputKind::Var, &seg.arguments) {
919 let t = &input.info_ty;
920 input.storage_ty = quote!(#core::var::BoxedVar<#t>);
921 }
922 }
923 "IntoValue" if !seg.arguments.is_empty() => {
924 if ty_from_generic(&mut input, errors, &t.ty, InputKind::Value, &seg.arguments) {
925 input.storage_ty = input.info_ty.clone();
926 }
927 }
928 "WidgetHandler" if !seg.arguments.is_empty() => {
929 if ty_from_generic(&mut input, errors, &t.ty, InputKind::WidgetHandler, &seg.arguments) {
930 let t = &input.info_ty;
931 input.storage_ty = quote!(#core::widget::builder::ArcWidgetHandler<#t>);
932 }
933 }
934 "UiNode" => {
935 input.kind = InputKind::UiNode;
936 input.ty = t.ty.to_token_stream();
937 input.info_ty = quote_spanned!(t.ty.span()=> #core::widget::node::BoxedUiNode);
938 input.storage_ty = quote!(#core::widget::node::ArcNode<#core::widget::node::BoxedUiNode>);
939 }
940 "UiNodeList" => {
941 input.kind = InputKind::UiNodeList;
942 input.ty = t.ty.to_token_stream();
943 input.info_ty = quote_spanned!(t.ty.span()=> #core::widget::node::BoxedUiNodeList);
944 input.storage_ty = quote!(#core::widget::node::ArcNodeList<#core::widget::node::BoxedUiNodeList>)
945 }
946 _ => {
947 errors.push("property input can only have impl types for: IntoVar<T>, IntoValue<T>, UiNode, WidgetHandler<A>, UiNodeList", seg.span());
948 }
949 }
950 }
951 t => {
952 errors.push("property input can only have `impl OneTrait`", t.span());
953 }
954 }
955 }
956 t => {
957 errors.push("property input can only have `impl OneTrait` types", t.span());
958 }
959 }
960 }
961 }
962 input
963 }
964}
965
966pub mod keyword {
967 syn::custom_keyword!(capture);
968 syn::custom_keyword!(widget_impl);
969}
970
971pub fn expand_meta(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
972 let args = parse_macro_input!(input as MetaArgs);
973 let core = crate_core();
974
975 let r = match args {
976 MetaArgs::Method { self_ty, sep, property } => {
977 let meta_ident = ident!("{}_", property);
978 quote! {
979 <#self_ty as #core::widget::base::WidgetImpl> #sep info_instance__() . #meta_ident()
980 }
981 }
982 MetaArgs::Function { path } => {
983 let ident = &path.segments.last().unwrap().ident;
984 let meta_ident = ident!("{}_", ident);
985
986 quote! {
987 <#core::widget::builder::WgtInfo as #path>::#meta_ident(&#core::widget::builder::WgtInfo)
988 }
989 }
990 };
991 r.into()
992}
993enum MetaArgs {
994 Method {
995 self_ty: Token![Self],
996 sep: Token![::],
997 property: Ident,
998 },
999 Function {
1000 path: Path,
1001 },
1002}
1003impl Parse for MetaArgs {
1004 fn parse(input: parse::ParseStream) -> Result<Self> {
1005 if input.peek(Token![Self]) {
1006 Ok(Self::Method {
1007 self_ty: input.parse()?,
1008 sep: input.parse()?,
1009 property: input.parse()?,
1010 })
1011 } else {
1012 Ok(Self::Function { path: input.parse()? })
1013 }
1014 }
1015}
1016
1017pub fn expand_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1018 let p = parse_macro_input!(input as PropertyImplArgs);
1019 if p.args.empty_or_trailing() {
1020 return quote_spanned! {p.path.span()=>
1021 compile_error!("missing args")
1022 }
1023 .into();
1024 }
1025
1026 let mut attrs = Attributes::new(p.attrs);
1027 attrs.tag_doc("P", "This method is a widget property");
1028
1029 let cfg = &attrs.cfg;
1030 let vis = p.vis;
1031 let path = p.path;
1032 let ident = &path.segments.last().unwrap().ident;
1033 let ident_unset = ident!("unset_{ident}");
1034 let ident_meta = ident!("{}_", ident);
1035 let ident_sorted = ident!("{}__", ident);
1036 let args = p.args;
1037 let mut sorted_args: Vec<_> = args.iter().collect();
1038 sorted_args.sort_by_key(|a| &a.ident);
1039
1040 let arg_idents = args.iter().map(|a| &a.ident);
1041 let sorted_idents: Vec<_> = sorted_args.iter().map(|a| &a.ident).collect();
1042 let sorted_tys = sorted_args.iter().map(|a| &a.ty);
1043
1044 let core = crate_core();
1045
1046 let r = quote! {
1047 #attrs
1048 #vis fn #ident(&self, #args) {
1049 #core::widget::base::WidgetImpl::base_ref(self).reexport__(|base__| {
1050 #path::#ident(base__, #(#arg_idents),*);
1051 });
1052 }
1053
1054 #cfg
1056 #[doc(hidden)]
1057 #[allow(dead_code)]
1058 #vis fn #ident_unset(&self) {
1059 #core::widget::base::WidgetImpl::base_ref(self).reexport__(|base__| {
1060 #path::#ident_unset(base__);
1061 });
1062 }
1063
1064 #cfg
1065 #[doc(hidden)]
1066 #[allow(dead_code)]
1067 #vis fn #ident_sorted(&self, #(#sorted_idents: #sorted_tys),*) {
1068 #core::widget::base::WidgetImpl::base_ref(self).reexport__(|base__| {
1069 #path::#ident_sorted(base__, #(#sorted_idents),*);
1070 });
1071 }
1072
1073 #cfg
1074 #[doc(hidden)]
1075 #[allow(dead_code)]
1076 #vis fn #ident_meta(&self) -> <#core::widget::builder::WgtInfo as #path>::MetaType {
1077 <#core::widget::builder::WgtInfo as #path>::#ident_meta(&#core::widget::builder::WgtInfo)
1078 }
1079 };
1080 r.into()
1081}
1082
1083struct PropertyImplArgs {
1084 attrs: Vec<Attribute>,
1085 vis: syn::Visibility,
1086 path: Path,
1087 args: Punctuated<SimpleFnArg, Token![,]>,
1088}
1089impl Parse for PropertyImplArgs {
1090 fn parse(input: parse::ParseStream) -> Result<Self> {
1091 Ok(Self {
1092 attrs: Attribute::parse_outer(&non_user_braced!(input, "attrs"))?,
1093 vis: non_user_braced!(input, "vis").parse().unwrap(),
1094 path: non_user_braced!(input, "path").parse()?,
1095 args: Punctuated::parse_terminated(&non_user_braced!(input, "args"))?,
1096 })
1097 }
1098}
1099
1100#[derive(Clone)]
1101struct SimpleFnArg {
1102 ident: Ident,
1103 _s: Token![:],
1104 ty: Type,
1105}
1106impl ToTokens for SimpleFnArg {
1107 fn to_tokens(&self, tokens: &mut TokenStream) {
1108 self.ident.to_tokens(tokens);
1109 self._s.to_tokens(tokens);
1110 self.ty.to_tokens(tokens);
1111 }
1112}
1113impl Parse for SimpleFnArg {
1114 fn parse(input: parse::ParseStream) -> Result<Self> {
1115 Ok(Self {
1116 ident: input.parse()?,
1117 _s: input.parse()?,
1118 ty: input.parse()?,
1119 })
1120 }
1121}