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    /// name of the property
14    pub name: syn::Ident,
15    #[allow(missing_docs)]
16    pub colon_token: Token![:],
17    /// Expression of the property
18    pub expr: syn::Expr,
19}
20
21/// Represent a transition definition
22///
23/// ```ignore
24///   State1 => State2 { statements; }
25/// ```
26///
27/// `State1` is stored in `source_state`,  `State2` in `destination_state` and `{ statements }` in action.
28#[derive(Debug)]
29pub struct TransitionDefinition {
30    /// Source state
31    pub source_state: syn::Ident,
32    #[allow(missing_docs)]
33    pub arrow_token: Token![=>],
34    /// Destination state
35    pub destination_state: syn::Ident,
36    /// Expression of the property
37    pub action: syn::Block,
38}
39
40impl Parse for TransitionDefinition {
41    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
42        let source_state: syn::Ident = input.parse()?;
43        let arrow_token: Token![=>] = input.parse()?;
44        let destination_state: syn::Ident = input.parse()?;
45        let action: syn::Block = input.parse()?;
46        Ok(TransitionDefinition {
47            source_state,
48            arrow_token,
49            destination_state,
50            action,
51        })
52    }
53}
54
55/// Represents a `on` statement in a component.
56///
57/// ```ignore
58///   on event_name: State1 => State2 { statements; }
59///   on event_name: [ State1 => State2 { statements; }, State3 => State4 { statements; } ]
60/// ```
61///
62/// In which case, the `event_name` is stored in the `name` field, while `State1` in `source_state`,
63/// `State2` in `destination_state` and `{ statements }` in action.
64#[derive(Debug)]
65pub struct OnValue {
66    #[allow(missing_docs)]
67    pub on_keyword: syn::Ident,
68    /// name of the on vale
69    pub name: syn::Ident,
70    #[allow(missing_docs)]
71    pub colon_token: Token![:],
72    /// Transition definitions for this on value
73    pub transition_definitions: syn::punctuated::Punctuated<TransitionDefinition, Token![,]>,
74}
75
76/// Intermediary structure used for parsing
77#[derive(Debug)]
78enum ComponentContent {
79    Id(syn::Ident),
80    Property(PropertyValue),
81    Child(ComponentInput),
82    On(OnValue),
83}
84
85/// Represents a component
86/// ```ignore
87/// Identifier
88/// {
89///   id: some_name
90///   prop1: value1
91///   Child
92///   {
93///     prop2: value2
94///   }
95/// }
96/// ```
97///
98/// `Identifier` is stored in the `name` field, while `prop1/value1` are in the list of `properties`.
99/// And `Child` is in the list of `children`.
100#[derive(Debug)]
101pub struct ComponentInput {
102    /// Name of the component
103    pub name: syn::Ident,
104    #[allow(missing_docs)]
105    pub brace_token: syn::token::Brace,
106    /// Identifier, used for storing in a variable
107    pub id: Option<syn::Ident>,
108    /// List of properties
109    pub properties: Vec<PropertyValue>,
110    /// List of children
111    pub children: Vec<ComponentInput>,
112    /// List of on_expressions
113    pub on_expressions: Vec<OnValue>,
114    /// Visibility of the component (not always applicable).
115    pub visibility: syn::Visibility,
116}
117
118impl Parse for ComponentContent {
119    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
120        if input.cursor().ident().map_or(false, |(i, _)| i == "on") && !input.peek2(Token![:]) {
121            let on_keyword: syn::Ident = input.parse()?;
122            let name: syn::Ident = input.parse()?;
123            let colon_token: Token![:] = input.parse()?;
124            let transition_definitions = if input.peek(syn::token::Bracket) {
125                let unparsed_content;
126                let _ = syn::bracketed!(unparsed_content in input);
127                unparsed_content.parse_terminated(TransitionDefinition::parse, Token![,])?
128            } else {
129                let mut p = syn::punctuated::Punctuated::<TransitionDefinition, Token![,]>::new();
130                p.push(TransitionDefinition::parse(input)?);
131                p
132            };
133
134            let on = OnValue {
135                on_keyword,
136                name,
137                colon_token,
138                transition_definitions,
139            };
140            Ok(ComponentContent::On(on))
141        } else if input.peek2(Token![:]) {
142            let lookahead1 = input.lookahead1();
143            if lookahead1.peek(syn::Ident) {
144                let name: syn::Ident = input.parse()?;
145                if name == "id" {
146                    let _: Token![:] = input.parse()?;
147                    let id: syn::Ident = input.parse()?;
148                    Ok(ComponentContent::Id(id))
149                } else {
150                    let colon_token: Token![:] = input.parse()?;
151                    let expr: syn::Expr = input.parse()?;
152                    Ok(ComponentContent::Property(PropertyValue {
153                        name,
154                        colon_token,
155                        expr,
156                    }))
157                }
158            } else {
159                Err(lookahead1.error())
160            }
161        } else {
162            Ok(Self::Child(ComponentInput::parse(input)?))
163        }
164    }
165}
166
167impl syn::parse::Parse for ComponentInput {
168    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
169        let visibility: syn::Visibility = input.parse()?;
170
171        let name = input.parse::<syn::Ident>()?;
172
173        let unparsed_content;
174        let brace_token = syn::braced!(unparsed_content in input);
175        let content = unparsed_content.parse_terminated(ComponentContent::parse, Token![,])?;
176
177        let mut id = Default::default();
178        let mut properties: Vec<PropertyValue> = Default::default();
179        let mut children: Vec<ComponentInput> = Default::default();
180        let mut on_expressions: Vec<OnValue> = Default::default();
181
182        for item in content {
183            match item {
184                ComponentContent::Id(idid) => id = Some(idid),
185                ComponentContent::Property(prop) => {
186                    properties.push(prop);
187                }
188                ComponentContent::Child(child) => {
189                    children.push(child);
190                }
191                ComponentContent::On(on_expr) => {
192                    on_expressions.push(on_expr);
193                }
194            }
195        }
196
197        Ok(Self {
198            name,
199            brace_token,
200            id,
201            properties,
202            children,
203            on_expressions,
204            visibility,
205        })
206    }
207}