use crate::diagnostics::*;
use syn::{
Expr, ExprLit, Ident, Lit, Pat, Result, Token,
parse::{Parse, ParseStream},
token,
};
pub enum RsxBody {
Single(RsxElement),
Fragment(Vec<RsxNode>),
}
pub struct RsxElement {
pub name: Ident,
pub attributes: Vec<RsxAttribute>,
pub children: Vec<RsxNode>,
}
pub enum RsxAttribute {
Flag(Ident),
Value { name: Ident, value: Expr },
When { condition: Expr, closure: Expr },
WhenSome { option: Expr, closure: Expr },
}
pub enum RsxNode {
Element(RsxElement),
Expr(Expr),
Spread(Expr),
For {
binding: Box<Pat>,
iter: Box<Expr>,
body: Vec<RsxNode>,
},
}
impl Parse for RsxBody {
fn parse(input: ParseStream) -> Result<Self> {
if input.peek(Token![<]) && input.peek2(Token![>]) {
input.parse::<Token![<]>()?;
input.parse::<Token![>]>()?;
let children = parse_children(input, None)?;
input.parse::<Token![<]>()?;
input.parse::<Token![/]>()?;
input.parse::<Token![>]>()?;
Ok(RsxBody::Fragment(children))
} else {
let element: RsxElement = input.parse()?;
Ok(RsxBody::Single(element))
}
}
}
impl Parse for RsxElement {
fn parse(input: ParseStream) -> Result<Self> {
input.parse::<Token![<]>()?;
let name: Ident = input.parse()?;
let mut attributes = Vec::with_capacity(4);
while !input.peek(Token![>]) && !input.peek(Token![/]) {
let attr_name: Ident = input.parse()?;
if input.peek(Token![=]) {
input.parse::<Token![=]>()?;
let value: Expr = if input.peek(token::Brace) {
let content;
syn::braced!(content in input);
content.parse()?
} else {
let lit: syn::Lit = input.parse()?;
syn::Expr::Lit(syn::ExprLit { attrs: vec![], lit })
};
if attr_name == "when" {
let (first, second) = parse_condition_tuple(value, "when")?;
attributes.push(RsxAttribute::When {
condition: first,
closure: second,
});
} else if attr_name == "whenSome" {
let (first, second) = parse_condition_tuple(value, "whenSome")?;
attributes.push(RsxAttribute::WhenSome {
option: first,
closure: second,
});
} else {
attributes.push(RsxAttribute::Value {
name: attr_name,
value,
});
}
} else {
attributes.push(RsxAttribute::Flag(attr_name));
}
}
let self_closing = if input.peek(Token![/]) {
input.parse::<Token![/]>()?;
input.parse::<Token![>]>()?;
true
} else {
input.parse::<Token![>]>()?;
false
};
let children = if self_closing {
Vec::new()
} else {
let children = parse_children(input, Some(&name))?;
input.parse::<Token![<]>()?;
input.parse::<Token![/]>()?;
let closing_name: Ident = input.parse()?;
input.parse::<Token![>]>()?;
if name != closing_name {
return Err(tag_mismatch_error(&closing_name, &name));
}
children
};
Ok(RsxElement {
name,
attributes,
children,
})
}
}
fn parse_children(input: ParseStream, parent_name: Option<&Ident>) -> Result<Vec<RsxNode>> {
let mut children = Vec::with_capacity(4);
loop {
if input.peek(Token![<]) && input.peek2(Token![/]) {
break;
}
if input.is_empty() {
return Err(match parent_name {
Some(name) => unclosed_tag_error(input.span(), name),
None => unclosed_fragment_error(input.span()),
});
}
if let Some(node) = try_parse_child_node(input)? {
children.push(node);
} else {
return Err(match parent_name {
Some(name) => invalid_child_in_tag_error(input.span(), name),
None => invalid_child_in_fragment_error(input.span()),
});
}
}
Ok(children)
}
fn try_parse_child_node(input: ParseStream) -> Result<Option<RsxNode>> {
if input.peek(token::Brace) {
let content;
syn::braced!(content in input);
if content.peek(Token![..]) {
content.parse::<Token![..]>()?;
content.parse::<Token![.]>()?;
let expr: Expr = content.parse()?;
Ok(Some(RsxNode::Spread(expr)))
} else if content.peek(Token![for]) {
Ok(Some(parse_for_loop(&content)?))
} else {
let expr: Expr = content.parse()?;
Ok(Some(RsxNode::Expr(expr)))
}
} else if input.peek(Token![<]) {
Ok(Some(RsxNode::Element(input.parse()?)))
} else if input.peek(syn::LitStr) {
let lit: syn::LitStr = input.parse()?;
Ok(Some(RsxNode::Expr(Expr::Lit(ExprLit {
attrs: vec![],
lit: Lit::Str(lit),
}))))
} else {
Ok(None)
}
}
fn parse_for_loop(content: ParseStream) -> Result<RsxNode> {
content.parse::<Token![for]>()?;
let binding: Pat = Pat::parse_single(content)?;
content.parse::<Token![in]>()?;
let mut iter_tokens = proc_macro2::TokenStream::new();
while !content.peek(token::Brace) {
if content.is_empty() {
return Err(for_loop_missing_brace_error(content.span()));
}
let tt: proc_macro2::TokenTree = content.parse()?;
iter_tokens.extend([tt]);
}
let iter_expr: Expr = syn::parse2(iter_tokens)?;
let body_content;
syn::braced!(body_content in content);
let mut body = Vec::with_capacity(2);
while !body_content.is_empty() {
if let Some(node) = try_parse_child_node(&body_content)? {
body.push(node);
} else {
return Err(for_loop_invalid_body_error(body_content.span()));
}
}
Ok(RsxNode::For {
binding: Box::new(binding),
iter: Box::new(iter_expr),
body,
})
}
fn parse_condition_tuple(value: Expr, attr_name: &str) -> Result<(Expr, Expr)> {
if let Expr::Tuple(tuple) = value {
if tuple.elems.len() == 2 {
let mut iter = tuple.elems.into_iter();
let first = iter
.next()
.expect("BUG: iterator should have first element after len() == 2 check");
let second = iter
.next()
.expect("BUG: iterator should have second element after len() == 2 check");
Ok((first, second))
} else {
Err(condition_tuple_wrong_count_error(
&tuple,
attr_name,
tuple.elems.len(),
))
}
} else {
Err(condition_tuple_wrong_type_error(&value, attr_name))
}
}