This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew_macro/derive_props/
builder.rs

1//! The `PropsBuilder` constructs props in alphabetical order and enforces that required props have
2//! been set before allowing the build to complete. Each property has a corresponding method in the
3//! builder. Required property builder methods advance the builder to the next step, optional
4//! properties can be added or skipped with no effect on the build step. Once all of required
5//! properties have been set, the builder moves to the final build step which implements the
6//! `build()` method.
7
8use 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}