This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew_macro/html_tree/
html_if.rs

1use 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}