brisk_it/
component.rs

1//! Data structure created from parsing a declarative component.
2use syn::{parse::Parse, Token};
3
4/// Represents a property in a component.
5///
6/// ```ignore
7///   identifier: some+expression
8/// ```
9///
10/// In which case, the `identifier` is stored in the `name` field, while `some+expression` is in `expr`.
11#[derive(Debug)]
12pub struct PropertyValue {
13    /// Mutability of the field, used only by a few components.
14    pub mutability: Option<Token![mut]>,
15    /// name of the property
16    pub name: syn::Ident,
17    #[allow(missing_docs)]
18    pub colon_token: Token![:],
19    /// Expression of the property
20    pub expr: syn::Expr,
21}
22
23#[derive(Debug)]
24
25/// Represent a match element.
26///
27/// ```ignore
28///  pattern => { some_expression }
29/// ```
30pub struct Match {
31    /// Pattern
32    pub pattern: syn::Pat,
33    #[allow(missing_docs)]
34    pub arrow_token: Token![=>],
35    /// Action
36    pub component: ComponentInput,
37}
38
39/// Represent a transition definition
40///
41/// ```ignore
42///   State1 => State2 { statements; }
43/// ```
44///
45/// `State1` is stored in `source_state`,  `State2` in `destination_state` and `{ statements }` in action.
46#[derive(Debug)]
47pub struct TransitionDefinition {
48    /// Source state
49    pub source_state: syn::Ident,
50    #[allow(missing_docs)]
51    pub arrow_token: Token![=>],
52    /// Destination state
53    pub destination_state: syn::Ident,
54    /// Action executed after the transition
55    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/// Represents a `on` statement in a component.
74///
75/// ```ignore
76///   on event_name: State1 => State2 { statements; }
77///   on event_name: [ State1 => State2 { statements; }, State3 => State4 { statements; } ]
78/// ```
79///
80/// In which case, the `event_name` is stored in the `name` field, while `State1` in `source_state`,
81/// `State2` in `destination_state` and `{ statements }` in action.
82#[derive(Debug)]
83pub struct OnValue {
84    #[allow(missing_docs)]
85    pub on_keyword: syn::Ident,
86    /// name of the on vale
87    pub name: syn::Ident,
88    #[allow(missing_docs)]
89    pub colon_token: Token![:],
90    /// Transition definitions for this on value
91    pub transition_definitions: syn::punctuated::Punctuated<TransitionDefinition, Token![,]>,
92}
93
94/// Intermediary structure used for parsing
95#[derive(Debug)]
96enum ComponentContent {
97    Id(syn::Ident),
98    Property(PropertyValue),
99    Child(ComponentInput),
100    On(OnValue),
101}
102
103/// Intermediary structure used for parsing
104#[derive(Debug)]
105enum MatchComponentContent {
106    Id(syn::Ident),
107    Property(PropertyValue),
108    Match(Match),
109}
110
111/// Represents a component
112/// ```ignore
113/// Identifier
114/// {
115///   id: some_name
116///   prop1: value1
117///   Child
118///   {
119///     prop2: value2
120///   }
121/// }
122/// ```
123///
124/// `Identifier` is stored in the `name` field, while `prop1/value1` are in the list of `properties`.
125/// And `Child` is in the list of `children`.
126#[derive(Debug)]
127pub struct ComponentInput {
128    /// Name of the component
129    pub name: syn::Ident,
130    #[allow(missing_docs)]
131    pub brace_token: syn::token::Brace,
132    /// Identifier, used for storing in a variable
133    pub id: Option<syn::Ident>,
134    /// List of properties
135    pub properties: Vec<PropertyValue>,
136    /// List of children
137    pub children: Vec<ComponentInput>,
138    /// List of on_expressions
139    pub on_expressions: Vec<OnValue>,
140    /// List of of_matches
141    pub matches: Vec<Match>,
142    /// Visibility of the component (not always applicable).
143    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}