yew_macro/derive_props/
builder.rs1use proc_macro2::{Ident, Span};
9use quote::{format_ident, quote, ToTokens};
10use syn::{parse_quote_spanned, Attribute, GenericParam};
11
12use super::generics::to_arguments;
13use super::DerivePropsInput;
14use crate::derive_props::generics::push_type_param;
15
16pub struct PropsBuilder<'a> {
17 builder_name: &'a Ident,
18 props: &'a DerivePropsInput,
19 wrapper_name: &'a Ident,
20 check_all_props_name: &'a Ident,
21 extra_attrs: &'a [Attribute],
22}
23
24impl ToTokens for PropsBuilder<'_> {
25 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
26 let Self {
27 builder_name,
28 props,
29 wrapper_name,
30 ..
31 } = self;
32
33 let DerivePropsInput { vis, generics, .. } = props;
34
35 let assert_all_props = self.impl_assert_props();
36
37 let (_, ty_generics, where_clause) = generics.split_for_impl();
38
39 let builder = quote! {
40 #[doc(hidden)]
41 #vis struct #builder_name #generics
42 #where_clause
43 {
44 wrapped: ::std::boxed::Box<#wrapper_name #ty_generics>,
45 }
46
47 #assert_all_props
48 };
49
50 tokens.extend(builder);
51 }
52}
53
54impl<'a> PropsBuilder<'_> {
55 pub fn new(
56 name: &'a Ident,
57 props: &'a DerivePropsInput,
58 wrapper_name: &'a Ident,
59 check_all_props_name: &'a Ident,
60 extra_attrs: &'a [Attribute],
61 ) -> PropsBuilder<'a> {
62 PropsBuilder {
63 builder_name: name,
64 props,
65 wrapper_name,
66 check_all_props_name,
67 extra_attrs,
68 }
69 }
70}
71
72impl PropsBuilder<'_> {
73 fn set_fields(&self) -> impl Iterator<Item = impl ToTokens + '_> {
74 self.props.prop_fields.iter().map(|pf| pf.to_field_setter())
75 }
76
77 fn impl_assert_props(&self) -> proc_macro2::TokenStream {
78 let Self {
79 builder_name,
80 check_all_props_name,
81 extra_attrs,
82 ..
83 } = self;
84 let DerivePropsInput {
85 vis,
86 generics,
87 props_name,
88 prop_fields,
89 ..
90 } = self.props;
91
92 let set_fields = self.set_fields();
93 let prop_fns = prop_fields
94 .iter()
95 .map(|pf| pf.to_build_step_fn(vis, props_name));
96
97 let (builder_impl_generics, ty_generics, builder_where_clause) = generics.split_for_impl();
98 let turbofish_generics = ty_generics.as_turbofish();
99 let generic_args = to_arguments(generics);
100
101 let mut assert_impl_generics = generics.clone();
102 let token_arg: GenericParam = parse_quote_spanned! {Span::mixed_site()=>
103 __YewToken
104 };
105 push_type_param(&mut assert_impl_generics, token_arg.clone());
106 let assert_impl_generics = assert_impl_generics;
107 let (impl_generics, _, where_clause) = assert_impl_generics.split_for_impl();
108
109 let props_mod_name = format_ident!("_{}", props_name, span = Span::mixed_site());
110 let mut check_impl_generics = assert_impl_generics.clone();
111 let mut check_args = vec![];
112 let mut check_props = proc_macro2::TokenStream::new();
113 let prop_field_decls = prop_fields
114 .iter()
115 .map(|pf| pf.to_field_check(props_name, vis, &token_arg))
116 .collect::<Vec<_>>();
117 let prop_name_decls = prop_field_decls.iter().map(|pf| pf.to_fake_prop_decl());
118 for pf in prop_field_decls.iter() {
119 check_props.extend(pf.to_stream(
120 &mut check_impl_generics,
121 &mut check_args,
122 &props_mod_name,
123 ));
124 }
125 let (check_impl_generics, _, check_where_clause) = check_impl_generics.split_for_impl();
126
127 quote! {
128 #[automatically_derived]
129 #( #extra_attrs )*
130 impl #builder_impl_generics #builder_name<#generic_args> #builder_where_clause {
131 #( #prop_fns )*
132 }
133
134 #[doc(hidden)]
135 #[allow(non_snake_case)]
136 #vis mod #props_mod_name {
137 #( #prop_name_decls )*
138 }
139 #check_props
140
141 #[doc(hidden)]
142 #vis struct #check_all_props_name<How>(::std::marker::PhantomData<How>);
143
144 #[automatically_derived]
145 impl<B, P, How> ::yew::html::HasProp<P, &dyn ::yew::html::HasProp<P, How>>
146 for #check_all_props_name<B>
147 where B: ::yew::html::HasProp<P, How> {}
148
149 #[automatically_derived]
150 impl #check_impl_generics ::yew::html::HasAllProps<
151 #props_name #ty_generics ,
152 ( #( #check_args , )* ),
153 > for #check_all_props_name< #token_arg > #check_where_clause {
154 }
155
156 #[automatically_derived]
157 impl #impl_generics ::yew::html::Buildable< #token_arg > for #builder_name<#generic_args> #where_clause {
158 type Output = #props_name #ty_generics;
159 type WrappedToken = #check_all_props_name< #token_arg >;
160 fn build(this: Self) -> Self::Output {
161 #props_name #turbofish_generics {
162 #(#set_fields)*
163 }
164 }
165 }
166 }
167 }
168}