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

yew_macro/
use_prepared_state.rs

1use proc_macro2::TokenStream;
2use quote::quote;
3use syn::parse::{Parse, ParseStream};
4use syn::{Expr, ExprClosure, ReturnType, Token, Type};
5
6#[derive(Debug)]
7pub struct PreparedState {
8    closure: ExprClosure,
9    return_type: Type,
10    deps: Expr,
11}
12
13impl Parse for PreparedState {
14    fn parse(input: ParseStream) -> syn::Result<Self> {
15        // Reads the deps.
16        let deps = input.parse()?;
17
18        input.parse::<Token![,]>().map_err(|e| {
19            syn::Error::new(
20                e.span(),
21                "this hook takes 2 arguments but 1 argument was supplied",
22            )
23        })?;
24
25        // Reads a closure.
26        let expr: Expr = input.parse()?;
27
28        let closure = match expr {
29            Expr::Closure(m) => m,
30            other => return Err(syn::Error::new_spanned(other, "expected closure")),
31        };
32
33        let return_type = match &closure.output {
34            ReturnType::Default => {
35                return Err(syn::Error::new_spanned(
36                    &closure,
37                    "You must specify a return type for this closure. This is used when the \
38                     closure is omitted from the client side rendering bundle.",
39                ))
40            }
41            ReturnType::Type(_rarrow, ty) => *ty.to_owned(),
42        };
43
44        if !input.is_empty() {
45            let maybe_trailing_comma = input.lookahead1();
46
47            if !maybe_trailing_comma.peek(Token![,]) {
48                return Err(maybe_trailing_comma.error());
49            }
50        }
51
52        Ok(Self {
53            closure,
54            return_type,
55            deps,
56        })
57    }
58}
59
60impl PreparedState {
61    // Async closure is not stable, so we rewrite it to closure + async block
62    #[cfg(not(nightly_yew))]
63    pub fn rewrite_to_closure_with_async_block(&self) -> ExprClosure {
64        use proc_macro2::Span;
65        use syn::parse_quote;
66
67        let async_token = match &self.closure.asyncness {
68            Some(m) => m,
69            None => return self.closure.clone(),
70        };
71
72        // The async block always need to be move so input can be moved into it.
73        let move_token = self
74            .closure
75            .capture
76            .unwrap_or_else(|| Token![move](Span::call_site()));
77        let body = &self.closure.body;
78
79        let inner = parse_quote! {
80            #async_token #move_token {
81                #body
82            }
83        };
84
85        let mut closure = self.closure.clone();
86
87        closure.asyncness = None;
88        // We omit the output type as it's an opaque future type.
89        closure.output = ReturnType::Default;
90
91        closure.body = inner;
92
93        closure.attrs.push(parse_quote! { #[allow(unused_braces)] });
94
95        closure
96    }
97
98    #[cfg(nightly_yew)]
99    pub fn rewrite_to_closure_with_async_block(&self) -> ExprClosure {
100        self.closure.clone()
101    }
102
103    pub fn to_token_stream_with_closure(&self) -> TokenStream {
104        let deps = &self.deps;
105        let rt = &self.return_type;
106        let closure = self.rewrite_to_closure_with_async_block();
107
108        match &self.closure.asyncness {
109            Some(_) => quote! {
110                ::yew::functional::use_prepared_state_with_suspension::<#rt, _, _, _>(#deps, #closure)
111            },
112            None => quote! {
113                ::yew::functional::use_prepared_state::<#rt, _, _>(#deps, #closure)
114            },
115        }
116    }
117
118    // Expose a hook for the client side.
119    //
120    // The closure is stripped from the client side.
121    pub fn to_token_stream_without_closure(&self) -> TokenStream {
122        let deps = &self.deps;
123        let rt = &self.return_type;
124
125        quote! {
126            ::yew::functional::use_prepared_state::<#rt, _>(#deps)
127        }
128    }
129}