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