feat: simple iterators and conditionals in rsx (#564)
* feat: simple iterators * fix: into_iter * feat: add support for unterminated conditionasl * fix: add tempalte mapping for helpers
This commit is contained in:
1 parent
0085b9cb1d
commit
8ea61e1b3e
3 files changed
+153
-2
No files matched your search
@@ -8,12 +8,24 @@ fn app(cx: Scope) -> Element {
|
|||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
// Use Map directly to lazily pull elements
|
// Use Map directly to lazily pull elements
|
||||||
(0..10).map(|f| rsx! { "{f}" }),
|
(0..10).map(|f| rsx! { "{f}" }),
|
||||||
|
|
||||||
// Collect into an intermediate collection if necessary
|
// Collect into an intermediate collection if necessary
|
||||||
["a", "b", "c"]
|
["a", "b", "c"]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|f| rsx! { "{f}" })
|
.map(|f| rsx! { "{f}" })
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
|
|
||||||
// Use optionals
|
// Use optionals
|
||||||
Some(rsx! { "Some" }),
|
Some(rsx! { "Some" }),
|
||||||
|
|
||||||
|
div {
|
||||||
|
for name in 0..10 {
|
||||||
|
rsx! { "{name}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if true {
|
||||||
|
rsx!{ "hello world!" }
|
||||||
|
}
|
||||||
|
}
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
+125
-2
@@ -5,7 +5,7 @@ use quote::{quote, ToTokens, TokenStreamExt};
|
|||||||
use syn::{
|
use syn::{
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
spanned::Spanned,
|
spanned::Spanned,
|
||||||
token, Expr, LitStr, Result,
|
token, Block, Expr, ExprIf, LitStr, Pat, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -20,6 +20,8 @@ Parse
|
|||||||
pub enum BodyNode {
|
pub enum BodyNode {
|
||||||
Element(Element),
|
Element(Element),
|
||||||
Component(Component),
|
Component(Component),
|
||||||
|
ForLoop(ForLoop),
|
||||||
|
IfChain(ExprIf),
|
||||||
Text(IfmtInput),
|
Text(IfmtInput),
|
||||||
RawExpr(Expr),
|
RawExpr(Expr),
|
||||||
}
|
}
|
||||||
@@ -35,6 +37,8 @@ impl BodyNode {
|
|||||||
BodyNode::Component(component) => component.name.span(),
|
BodyNode::Component(component) => component.name.span(),
|
||||||
BodyNode::Text(text) => text.source.span(),
|
BodyNode::Text(text) => text.source.span(),
|
||||||
BodyNode::RawExpr(exp) => exp.span(),
|
BodyNode::RawExpr(exp) => exp.span(),
|
||||||
|
BodyNode::ForLoop(fl) => fl.for_token.span(),
|
||||||
|
BodyNode::IfChain(f) => f.if_token.span(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,11 +87,32 @@ impl Parse for BodyNode {
|
|||||||
// crate::Input::<InputProps<'_, i32> {}
|
// crate::Input::<InputProps<'_, i32> {}
|
||||||
if body_stream.peek(token::Brace) {
|
if body_stream.peek(token::Brace) {
|
||||||
Component::validate_component_path(&path)?;
|
Component::validate_component_path(&path)?;
|
||||||
|
|
||||||
return Ok(BodyNode::Component(stream.parse()?));
|
return Ok(BodyNode::Component(stream.parse()?));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transform for loops into into_iter calls
|
||||||
|
if stream.peek(Token![for]) {
|
||||||
|
let _f = stream.parse::<Token![for]>()?;
|
||||||
|
let pat = stream.parse::<Pat>()?;
|
||||||
|
let _i = stream.parse::<Token![in]>()?;
|
||||||
|
let expr = stream.parse::<Box<Expr>>()?;
|
||||||
|
let body = stream.parse::<Block>()?;
|
||||||
|
|
||||||
|
return Ok(BodyNode::ForLoop(ForLoop {
|
||||||
|
for_token: _f,
|
||||||
|
pat,
|
||||||
|
in_token: _i,
|
||||||
|
expr,
|
||||||
|
body,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform unterminated if statements into terminated optional if statements
|
||||||
|
if stream.peek(Token![if]) {
|
||||||
|
return Ok(BodyNode::IfChain(stream.parse()?));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(BodyNode::RawExpr(stream.parse::<Expr>()?))
|
Ok(BodyNode::RawExpr(stream.parse::<Expr>()?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,6 +128,104 @@ impl ToTokens for BodyNode {
|
|||||||
BodyNode::RawExpr(exp) => tokens.append_all(quote! {
|
BodyNode::RawExpr(exp) => tokens.append_all(quote! {
|
||||||
__cx.fragment_from_iter(#exp)
|
__cx.fragment_from_iter(#exp)
|
||||||
}),
|
}),
|
||||||
|
BodyNode::ForLoop(exp) => {
|
||||||
|
let ForLoop {
|
||||||
|
pat, expr, body, ..
|
||||||
|
} = exp;
|
||||||
|
|
||||||
|
tokens.append_all(quote! {
|
||||||
|
__cx.fragment_from_iter(
|
||||||
|
(#expr).into_iter().map(|#pat| {
|
||||||
|
#body
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
BodyNode::IfChain(chain) => {
|
||||||
|
if is_if_chain_terminated(chain) {
|
||||||
|
tokens.append_all(quote! {
|
||||||
|
__cx.fragment_from_iter(#chain)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let ExprIf {
|
||||||
|
cond,
|
||||||
|
then_branch,
|
||||||
|
else_branch,
|
||||||
|
..
|
||||||
|
} = chain;
|
||||||
|
|
||||||
|
let mut body = TokenStream2::new();
|
||||||
|
|
||||||
|
body.append_all(quote! {
|
||||||
|
if #cond {
|
||||||
|
Some(#then_branch)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut elif = else_branch;
|
||||||
|
|
||||||
|
while let Some((_, ref branch)) = elif {
|
||||||
|
match branch.as_ref() {
|
||||||
|
Expr::If(ref eelif) => {
|
||||||
|
let ExprIf {
|
||||||
|
cond,
|
||||||
|
then_branch,
|
||||||
|
else_branch,
|
||||||
|
..
|
||||||
|
} = eelif;
|
||||||
|
|
||||||
|
body.append_all(quote! {
|
||||||
|
else if #cond {
|
||||||
|
Some(#then_branch)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
elif = else_branch;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
body.append_all(quote! {
|
||||||
|
else {
|
||||||
|
#branch
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body.append_all(quote! {
|
||||||
|
else { None }
|
||||||
|
});
|
||||||
|
|
||||||
|
tokens.append_all(quote! {
|
||||||
|
__cx.fragment_from_iter(#body)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
||||||
|
pub struct ForLoop {
|
||||||
|
pub for_token: Token![for],
|
||||||
|
pub pat: Pat,
|
||||||
|
pub in_token: Token![in],
|
||||||
|
pub expr: Box<Expr>,
|
||||||
|
pub body: Block,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_if_chain_terminated(chain: &ExprIf) -> bool {
|
||||||
|
let mut current = chain;
|
||||||
|
loop {
|
||||||
|
if let Some((_, else_block)) = ¤t.else_branch {
|
||||||
|
if let Expr::If(else_if) = else_block.as_ref() {
|
||||||
|
current = else_if;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -557,6 +557,22 @@ impl TemplateBuilder {
|
|||||||
fully_static: false,
|
fully_static: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
BodyNode::ForLoop(expr) => {
|
||||||
|
self.nodes.push(TemplateNodeBuilder {
|
||||||
|
id,
|
||||||
|
node_type: TemplateNodeTypeBuilder::DynamicNode(
|
||||||
|
self.dynamic_context.add_node(BodyNode::ForLoop(expr)),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
BodyNode::IfChain(expr) => {
|
||||||
|
self.nodes.push(TemplateNodeBuilder {
|
||||||
|
id,
|
||||||
|
node_type: TemplateNodeTypeBuilder::DynamicNode(
|
||||||
|
self.dynamic_context.add_node(BodyNode::IfChain(expr)),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user