yew_macro/html_tree/
html_if.rs1use proc_macro2::TokenStream;
2use quote::{quote_spanned, ToTokens};
3use syn::buffer::Cursor;
4use syn::parse::{Parse, ParseStream};
5use syn::spanned::Spanned;
6use syn::{Expr, Token};
7
8use super::{HtmlRootBraced, ToNodeIterator};
9use crate::PeekValue;
10
11pub struct HtmlIf {
12 if_token: Token![if],
13 cond: Box<Expr>,
14 then_branch: HtmlRootBraced,
15 else_branch: Option<(Token![else], Box<HtmlRootBracedOrIf>)>,
16}
17
18impl PeekValue<()> for HtmlIf {
19 fn peek(cursor: Cursor) -> Option<()> {
20 let (ident, _) = cursor.ident()?;
21 (ident == "if").then_some(())
22 }
23}
24
25impl Parse for HtmlIf {
26 fn parse(input: ParseStream) -> syn::Result<Self> {
27 let if_token = input.parse()?;
28 let cond = Box::new(input.call(Expr::parse_without_eager_brace)?);
29 match &*cond {
30 Expr::Block(syn::ExprBlock { block, .. }) if block.stmts.is_empty() => {
31 return Err(syn::Error::new(
32 cond.span(),
33 "missing condition for `if` expression",
34 ))
35 }
36 _ => {}
37 }
38 if input.is_empty() {
39 return Err(syn::Error::new(
40 cond.span(),
41 "this `if` expression has a condition, but no block",
42 ));
43 }
44
45 let then_branch = input.parse()?;
46 let else_branch = input
47 .parse::<Token![else]>()
48 .ok()
49 .map(|else_token| {
50 if input.is_empty() {
51 return Err(syn::Error::new(
52 else_token.span(),
53 "expected block or `if` after `else`",
54 ));
55 }
56
57 input.parse().map(|branch| (else_token, branch))
58 })
59 .transpose()?;
60
61 Ok(HtmlIf {
62 if_token,
63 cond,
64 then_branch,
65 else_branch,
66 })
67 }
68}
69
70impl ToTokens for HtmlIf {
71 fn to_tokens(&self, tokens: &mut TokenStream) {
72 let HtmlIf {
73 if_token,
74 cond,
75 then_branch,
76 else_branch,
77 } = self;
78 let default_else_branch = syn::parse_quote! { {} };
79 let else_branch = else_branch
80 .as_ref()
81 .map(|(_, branch)| branch)
82 .unwrap_or(&default_else_branch);
83 let new_tokens = quote_spanned! {if_token.span()=>
84 if #cond #then_branch else #else_branch
85 };
86
87 tokens.extend(new_tokens);
88 }
89}
90
91impl ToNodeIterator for HtmlIf {
92 fn to_node_iterator_stream(&self) -> Option<TokenStream> {
93 let HtmlIf {
94 if_token,
95 cond,
96 then_branch,
97 else_branch,
98 } = self;
99 let default_else_branch = syn::parse_str("{}").unwrap();
100 let else_branch = else_branch
101 .as_ref()
102 .map(|(_, branch)| branch)
103 .unwrap_or(&default_else_branch);
104 let new_tokens = quote_spanned! {if_token.span()=>
105 if #cond #then_branch else #else_branch
106 };
107
108 Some(quote_spanned! {if_token.span=> #new_tokens})
109 }
110}
111
112pub enum HtmlRootBracedOrIf {
113 Branch(HtmlRootBraced),
114 If(HtmlIf),
115}
116
117impl PeekValue<()> for HtmlRootBracedOrIf {
118 fn peek(cursor: Cursor) -> Option<()> {
119 HtmlRootBraced::peek(cursor).or_else(|| HtmlIf::peek(cursor))
120 }
121}
122
123impl Parse for HtmlRootBracedOrIf {
124 fn parse(input: ParseStream) -> syn::Result<Self> {
125 if HtmlRootBraced::peek(input.cursor()).is_some() {
126 input.parse().map(Self::Branch)
127 } else {
128 input.parse().map(Self::If)
129 }
130 }
131}
132
133impl ToTokens for HtmlRootBracedOrIf {
134 fn to_tokens(&self, tokens: &mut TokenStream) {
135 match self {
136 Self::Branch(x) => x.to_tokens(tokens),
137 Self::If(x) => x.to_tokens(tokens),
138 }
139 }
140}