1use crate::location::DynIdx;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::quote;
4use quote::{ToTokens, TokenStreamExt};
5use syn::{
6 parse::{Parse, ParseStream},
7 token::Brace,
8 Expr, Result, Token,
9};
10
11use crate::TemplateBody;
12
13#[non_exhaustive]
14#[derive(PartialEq, Eq, Clone, Debug)]
15pub struct IfChain {
16 pub if_token: Token![if],
17 pub cond: Box<Expr>,
18 pub then_brace: Brace,
19 pub then_branch: TemplateBody,
20 pub else_if_branch: Option<Box<IfChain>>,
21 pub else_brace: Option<Brace>,
22 pub else_branch: Option<TemplateBody>,
23 pub dyn_idx: DynIdx,
24}
25
26impl IfChain {
27 pub fn for_each_branch(&self, f: &mut impl FnMut(&TemplateBody)) {
28 f(&self.then_branch);
29
30 if let Some(else_if) = &self.else_if_branch {
31 else_if.for_each_branch(f);
32 }
33
34 if let Some(else_branch) = &self.else_branch {
35 f(else_branch);
36 }
37 }
38}
39
40impl Parse for IfChain {
41 fn parse(input: ParseStream) -> Result<Self> {
42 let if_token: Token![if] = input.parse()?;
43
44 let cond = Box::new(input.call(Expr::parse_without_eager_brace)?);
46
47 let content;
48 let then_brace = syn::braced!(content in input);
49
50 let then_branch = content.parse()?;
51
52 let mut else_brace = None;
53 let mut else_branch = None;
54 let mut else_if_branch = None;
55
56 if input.peek(Token![else]) {
58 input.parse::<Token![else]>()?;
59 if input.peek(Token![if]) {
60 else_if_branch = Some(Box::new(input.parse::<IfChain>()?));
61 } else {
62 let content;
63 else_brace = Some(syn::braced!(content in input));
64 else_branch = Some(content.parse()?);
65 }
66 }
67
68 Ok(Self {
69 cond,
70 if_token,
71 then_branch,
72 else_if_branch,
73 else_branch,
74 then_brace,
75 else_brace,
76 dyn_idx: DynIdx::default(),
77 })
78 }
79}
80
81impl ToTokens for IfChain {
82 fn to_tokens(&self, tokens: &mut TokenStream2) {
83 let mut body = TokenStream2::new();
84 let mut terminated = false;
85
86 let mut elif = Some(self);
87
88 while let Some(chain) = elif {
89 let IfChain {
90 if_token,
91 cond,
92 then_branch,
93 else_if_branch,
94 else_branch,
95 ..
96 } = chain;
97
98 body.append_all(quote! {
99 #if_token #cond {
100 { #then_branch }
101 }
102 });
103
104 if let Some(next) = else_if_branch {
105 body.append_all(quote! {
106 else
107 });
108 elif = Some(next);
109 } else if let Some(else_branch) = else_branch {
110 body.append_all(quote! {
111 else {
112 {#else_branch}
113 }
114 });
115 terminated = true;
116 break;
117 } else {
118 elif = None;
119 }
120 }
121
122 if !terminated {
123 body.append_all(quote! {
124 else { dioxus_core::VNode::empty() }
125 });
126 }
127
128 tokens.append_all(quote! {
129 {
130 let ___nodes = (#body).into_dyn_node();
131 ___nodes
132 }
133 })
134 }
135}
136
137#[test]
138fn parses_if_chain() {
139 let input = quote! {
140 if true {
141 "one"
142 } else if false {
143 "two"
144 } else {
145 "three"
146 }
147 };
148
149 let _chain: IfChain = syn::parse2(input).unwrap();
150}