110 lines
3.2 KiB
Rust
110 lines
3.2 KiB
Rust
use super::*;
|
|
|
|
use proc_macro2::{Span, TokenStream as TokenStream2};
|
|
use quote::{quote, ToTokens, TokenStreamExt};
|
|
use syn::{
|
|
parse::{Parse, ParseStream},
|
|
spanned::Spanned,
|
|
token, Expr, LitStr, Result,
|
|
};
|
|
|
|
/*
|
|
Parse
|
|
-> div {}
|
|
-> Component {}
|
|
-> component()
|
|
-> "text {with_args}"
|
|
-> (0..10).map(|f| rsx!("asd")), // <--- notice the comma - must be a complete expr
|
|
*/
|
|
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
|
pub enum BodyNode {
|
|
Element(Element),
|
|
Component(Component),
|
|
Text(IfmtInput),
|
|
RawExpr(Expr),
|
|
}
|
|
|
|
impl BodyNode {
|
|
pub fn is_litstr(&self) -> bool {
|
|
matches!(self, BodyNode::Text(_))
|
|
}
|
|
|
|
pub fn span(&self) -> Span {
|
|
match self {
|
|
BodyNode::Element(el) => el.name.span(),
|
|
BodyNode::Component(component) => component.name.span(),
|
|
BodyNode::Text(text) => text.source.span(),
|
|
BodyNode::RawExpr(exp) => exp.span(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Parse for BodyNode {
|
|
fn parse(stream: ParseStream) -> Result<Self> {
|
|
if stream.peek(LitStr) {
|
|
return Ok(BodyNode::Text(stream.parse()?));
|
|
}
|
|
|
|
let body_stream = stream.fork();
|
|
if let Ok(path) = body_stream.parse::<syn::Path>() {
|
|
// this is an Element if path match of:
|
|
// - one ident
|
|
// - followed by `{`
|
|
// - 1st char is lowercase
|
|
// - no underscores (reserved for components)
|
|
//
|
|
// example:
|
|
// div {}
|
|
if let Some(ident) = path.get_ident() {
|
|
let el_name = ident.to_string();
|
|
|
|
let first_char = el_name.chars().next().unwrap();
|
|
|
|
if body_stream.peek(token::Brace)
|
|
&& first_char.is_ascii_lowercase()
|
|
&& !el_name.contains('_')
|
|
{
|
|
return Ok(BodyNode::Element(stream.parse::<Element>()?));
|
|
}
|
|
}
|
|
|
|
// Otherwise this should be Component, allowed syntax:
|
|
// - syn::Path
|
|
// - PathArguments can only apper in last segment
|
|
// - followed by `{` or `(`, note `(` cannot be used with one ident
|
|
//
|
|
// example
|
|
// Div {}
|
|
// ::Div {}
|
|
// crate::Div {}
|
|
// component {} <-- already handled by elements
|
|
// ::component {}
|
|
// crate::component{}
|
|
// Input::<InputProps<'_, i32> {}
|
|
// crate::Input::<InputProps<'_, i32> {}
|
|
if body_stream.peek(token::Brace) {
|
|
Component::validate_component_path(&path)?;
|
|
|
|
return Ok(BodyNode::Component(stream.parse()?));
|
|
}
|
|
}
|
|
|
|
Ok(BodyNode::RawExpr(stream.parse::<Expr>()?))
|
|
}
|
|
}
|
|
|
|
impl ToTokens for BodyNode {
|
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
|
match &self {
|
|
BodyNode::Element(el) => el.to_tokens(tokens),
|
|
BodyNode::Component(comp) => comp.to_tokens(tokens),
|
|
BodyNode::Text(txt) => tokens.append_all(quote! {
|
|
__cx.text(#txt)
|
|
}),
|
|
BodyNode::RawExpr(exp) => tokens.append_all(quote! {
|
|
__cx.fragment_from_iter(#exp)
|
|
}),
|
|
}
|
|
}
|
|
}
|