forked from yewstack/yew
-
Notifications
You must be signed in to change notification settings - Fork 0
/
use_prepared_state.rs
129 lines (106 loc) · 3.67 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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use proc_macro2::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{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 expr: Expr = input.parse()?;
let closure = match expr {
Expr::Closure(m) => m,
other => return Err(syn::Error::new_spanned(other, "expected closure")),
};
input.parse::<Token![,]>().map_err(|e| {
syn::Error::new(
e.span(),
"this hook takes 2 arguments but 1 argument was supplied",
)
})?;
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 closure + async block
#[cfg(not(feature = "nightly"))]
pub fn rewrite_to_closure_with_async_block(&self) -> ExprClosure {
use proc_macro2::Span;
use syn::parse_quote;
let async_token = match &self.closure.asyncness {
Some(m) => m,
None => return self.closure.clone(),
};
// The async block always need to be move so input can be moved into it.
let move_token = self
.closure
.capture
.unwrap_or_else(|| Token![move](Span::call_site()));
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
}
#[cfg(feature = "nightly")]
pub fn rewrite_to_closure_with_async_block(&self) -> ExprClosure {
self.closure.clone()
}
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)
}
}
}