1use syn::{parse::Parse, Token};
3
4#[derive(Debug)]
12pub struct PropertyValue {
13 pub mutability: Option<Token![mut]>,
15 pub name: syn::Ident,
17 #[allow(missing_docs)]
18 pub colon_token: Token![:],
19 pub expr: syn::Expr,
21}
22
23#[derive(Debug)]
24
25pub struct Match {
31 pub pattern: syn::Pat,
33 #[allow(missing_docs)]
34 pub arrow_token: Token![=>],
35 pub component: ComponentInput,
37}
38
39#[derive(Debug)]
47pub struct TransitionDefinition {
48 pub source_state: syn::Ident,
50 #[allow(missing_docs)]
51 pub arrow_token: Token![=>],
52 pub destination_state: syn::Ident,
54 pub action: syn::Block,
56}
57
58impl Parse for TransitionDefinition {
59 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
60 let source_state: syn::Ident = input.parse()?;
61 let arrow_token: Token![=>] = input.parse()?;
62 let destination_state: syn::Ident = input.parse()?;
63 let action: syn::Block = input.parse()?;
64 Ok(TransitionDefinition {
65 source_state,
66 arrow_token,
67 destination_state,
68 action,
69 })
70 }
71}
72
73#[derive(Debug)]
83pub struct OnValue {
84 #[allow(missing_docs)]
85 pub on_keyword: syn::Ident,
86 pub name: syn::Ident,
88 #[allow(missing_docs)]
89 pub colon_token: Token![:],
90 pub transition_definitions: syn::punctuated::Punctuated<TransitionDefinition, Token![,]>,
92}
93
94#[derive(Debug)]
96enum ComponentContent {
97 Id(syn::Ident),
98 Property(PropertyValue),
99 Child(ComponentInput),
100 On(OnValue),
101}
102
103#[derive(Debug)]
105enum MatchComponentContent {
106 Id(syn::Ident),
107 Property(PropertyValue),
108 Match(Match),
109}
110
111#[derive(Debug)]
127pub struct ComponentInput {
128 pub name: syn::Ident,
130 #[allow(missing_docs)]
131 pub brace_token: syn::token::Brace,
132 pub id: Option<syn::Ident>,
134 pub properties: Vec<PropertyValue>,
136 pub children: Vec<ComponentInput>,
138 pub on_expressions: Vec<OnValue>,
140 pub matches: Vec<Match>,
142 pub visibility: syn::Visibility,
144}
145
146impl Parse for MatchComponentContent {
147 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
148 if input.peek2(Token![:]) || (input.peek(Token![mut]) && input.peek3(Token![:])) {
149 let mutability = input.parse()?;
150 let lookahead1 = input.lookahead1();
151 if lookahead1.peek(syn::Ident) {
152 let r = input.parse();
153 let name: syn::Ident = r?;
154 if name == "id" {
155 let _: Token![:] = input.parse()?;
156 let id: syn::Ident = input.parse()?;
157 Ok(MatchComponentContent::Id(id))
158 } else {
159 let colon_token: Token![:] = input.parse()?;
160 let expr: syn::Expr = input.parse()?;
161 Ok(MatchComponentContent::Property(PropertyValue {
162 mutability,
163 name,
164 colon_token,
165 expr,
166 }))
167 }
168 } else {
169 let error = lookahead1.error();
170 Err(error)
171 }
172 } else {
173 let pattern = syn::Pat::parse_multi(input)?;
174 let arrow_token = input.parse()?;
175 let component = input.parse()?;
176 Ok(MatchComponentContent::Match(Match {
177 pattern,
178 arrow_token,
179 component,
180 }))
181 }
182 }
183}
184
185impl Parse for ComponentContent {
186 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
187 if input.cursor().ident().map_or(false, |(i, _)| i == "on") && !input.peek2(Token![:]) {
188 let on_keyword: syn::Ident = input.parse()?;
189 let name: syn::Ident = input.parse()?;
190 let colon_token: Token![:] = input.parse()?;
191 let transition_definitions = if input.peek(syn::token::Bracket) {
192 let unparsed_content;
193 let _ = syn::bracketed!(unparsed_content in input);
194 unparsed_content.parse_terminated(TransitionDefinition::parse, Token![,])?
195 } else {
196 let mut p = syn::punctuated::Punctuated::<TransitionDefinition, Token![,]>::new();
197 p.push(TransitionDefinition::parse(input)?);
198 p
199 };
200
201 let on = OnValue {
202 on_keyword,
203 name,
204 colon_token,
205 transition_definitions,
206 };
207 Ok(ComponentContent::On(on))
208 } else if input.peek2(Token![:]) || (input.peek(Token![mut]) && input.peek3(Token![:])) {
209 let mutability = input.parse()?;
210 let lookahead1 = input.lookahead1();
211 if lookahead1.peek(syn::Ident) {
212 let r = input.parse();
213 let name: syn::Ident = r?;
214 if name == "id" {
215 let _: Token![:] = input.parse()?;
216 let id: syn::Ident = input.parse()?;
217 Ok(ComponentContent::Id(id))
218 } else {
219 let colon_token: Token![:] = input.parse()?;
220 let expr: syn::Expr = input.parse()?;
221 Ok(ComponentContent::Property(PropertyValue {
222 mutability,
223 name,
224 colon_token,
225 expr,
226 }))
227 }
228 } else {
229 let error = lookahead1.error();
230 Err(error)
231 }
232 } else {
233 Ok(Self::Child(ComponentInput::parse(input)?))
234 }
235 }
236}
237
238impl syn::parse::Parse for ComponentInput {
239 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
240 let visibility: syn::Visibility = input.parse()?;
241
242 let name = input.parse::<syn::Ident>()?;
243
244 let unparsed_content;
245 let brace_token = syn::braced!(unparsed_content in input);
246
247 let mut id = Default::default();
248 let mut properties: Vec<PropertyValue> = Default::default();
249 let mut children: Vec<ComponentInput> = Default::default();
250 let mut on_expressions: Vec<OnValue> = Default::default();
251 let mut matches: Vec<Match> = Default::default();
252
253 if name == "Match" {
254 let content =
255 unparsed_content.parse_terminated(MatchComponentContent::parse, Token![,])?;
256 for item in content {
257 match item {
258 MatchComponentContent::Id(idid) => id = Some(idid),
259 MatchComponentContent::Property(prop) => {
260 properties.push(prop);
261 }
262 MatchComponentContent::Match(match_) => {
263 matches.push(match_);
264 }
265 }
266 }
267 } else {
268 let content = unparsed_content.parse_terminated(ComponentContent::parse, Token![,])?;
269 for item in content {
270 match item {
271 ComponentContent::Id(idid) => id = Some(idid),
272 ComponentContent::Property(prop) => {
273 properties.push(prop);
274 }
275 ComponentContent::Child(child) => {
276 children.push(child);
277 }
278 ComponentContent::On(on_expr) => {
279 on_expressions.push(on_expr);
280 }
281 }
282 }
283 }
284
285 Ok(Self {
286 name,
287 brace_token,
288 id,
289 properties,
290 children,
291 on_expressions,
292 visibility,
293 matches,
294 })
295 }
296}