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