New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
use_prepared_state
& use_transitive_state
#2650
Changes from 19 commits
7aa57b0
94651f5
e7a55a3
0b378d4
0c6924c
de3e6d4
fe09c93
109fcfa
5f731b6
cd9f735
95bb84d
5365ffd
8c659db
3657129
b9aa42e
72253e5
6a676a5
3faa58c
ee5caea
1ff916a
59f0a0b
566ecb8
2344663
337a31b
a5ec89f
e3d38ed
53b6d68
1a79e84
335039f
b0c8632
8503d24
1f0b5e1
268cf57
92ebe1a
5784ec7
7e12d61
b8267ef
f01c200
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
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<const WITH_ASYNC_CLOSURE: bool> { | ||
closure: ExprClosure, | ||
return_type: Type, | ||
deps: Expr, | ||
} | ||
|
||
impl<const WITH_ASYNC_CLOSURE: bool> Parse for PreparedState<WITH_ASYNC_CLOSURE> { | ||
fn parse(input: ParseStream) -> syn::Result<Self> { | ||
// Reads a closure. | ||
let closure: ExprClosure = input.parse()?; | ||
|
||
input.parse::<Token![,]>()?; | ||
|
||
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()); | ||
} | ||
} | ||
|
||
if !WITH_ASYNC_CLOSURE { | ||
if let Some(m) = closure.asyncness.as_ref() { | ||
return Err(syn::Error::new_spanned( | ||
&m, | ||
"You need to enable feature tokio to use async closure under non-wasm32 \ | ||
targets.", | ||
)); | ||
} | ||
} | ||
|
||
Ok(Self { | ||
closure, | ||
return_type, | ||
deps, | ||
}) | ||
} | ||
} | ||
|
||
impl<const WITH_ASYNC_CLOSURE: bool> PreparedState<WITH_ASYNC_CLOSURE> { | ||
// 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; | ||
|
||
match &self.closure.asyncness { | ||
Some(_) => quote! { | ||
::yew::functional::use_prepared_state_with_suspension::<#rt, _>(#deps) | ||
}, | ||
None => quote! { | ||
::yew::functional::use_prepared_state::<#rt, _>(#deps) | ||
}, | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
use proc_macro2::TokenStream; | ||
use quote::quote; | ||
use syn::parse::{Parse, ParseStream}; | ||
use syn::{Expr, ExprClosure, ReturnType, Token, Type}; | ||
|
||
#[derive(Debug)] | ||
pub struct TransitiveState { | ||
closure: ExprClosure, | ||
return_type: Type, | ||
deps: Expr, | ||
} | ||
|
||
impl Parse for TransitiveState { | ||
fn parse(input: ParseStream) -> syn::Result<Self> { | ||
// Reads a closure. | ||
let closure: ExprClosure = input.parse()?; | ||
|
||
input.parse::<Token![,]>()?; | ||
|
||
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 TransitiveState { | ||
pub fn to_token_stream_with_closure(&self) -> TokenStream { | ||
let deps = &self.deps; | ||
let rt = &self.return_type; | ||
let closure = &self.closure; | ||
|
||
quote! { | ||
::yew::functional::use_transitive_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_transitive_state::<#rt, _>(#deps) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,9 @@ thiserror = "1.0" | |
|
||
futures = { version = "0.3", optional = true } | ||
html-escape = { version = "0.2.9", optional = true } | ||
base64ct = { version = "1.5.0", features = ["std"], optional = true } | ||
bincode = { version = "1.3.3", optional = true } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not blocking but it would be nice if we could change the implementation, similar to how the global allocator can be changed. If one wants to use JSON, BSON, etc for this, they should have the option to do so There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should create a separate issue for this. Can you create one when merging this PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. |
||
serde = { version = "1", features = ["derive"] } | ||
|
||
[dependencies.web-sys] | ||
version = "0.3" | ||
|
@@ -61,6 +64,7 @@ features = [ | |
"UiEvent", | ||
"WheelEvent", | ||
"Window", | ||
"HtmlScriptElement", | ||
] | ||
|
||
[target.'cfg(target_arch = "wasm32")'.dependencies] | ||
|
@@ -87,9 +91,9 @@ features = [ | |
] | ||
|
||
[features] | ||
ssr = ["futures", "html-escape"] | ||
ssr = ["futures", "html-escape", "base64ct", "bincode"] | ||
csr = [] | ||
hydration = ["csr"] | ||
hydration = ["csr", "base64ct", "bincode"] | ||
trace_hydration = ["hydration"] | ||
doc_test = ["csr", "hydration", "ssr"] | ||
wasm_test = ["csr", "hydration", "ssr"] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another case of #2681