Merge remote-tracking branch 'upstream/master' into jk/rsx-refactor
This commit is contained in:
commit
9e7e5b0859
22 files changed
+358
-324
No files matched your search
@@ -18,20 +18,59 @@ use quote::{quote, ToTokens, TokenStreamExt};
|
||||
use syn::{
|
||||
ext::IdentExt,
|
||||
parse::{Parse, ParseBuffer, ParseStream},
|
||||
token, Error, Expr, Ident, LitStr, Result, Token,
|
||||
spanned::Spanned,
|
||||
token, AngleBracketedGenericArguments, Error, Expr, Ident, LitStr, PathArguments, Result,
|
||||
Token,
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct Component {
|
||||
pub name: syn::Path,
|
||||
pub prop_gen_args: Option<AngleBracketedGenericArguments>,
|
||||
pub body: Vec<ComponentField>,
|
||||
pub children: Vec<BodyNode>,
|
||||
pub manual_props: Option<Expr>,
|
||||
}
|
||||
|
||||
impl Component {
|
||||
pub fn validate_component_path(path: &syn::Path) -> Result<()> {
|
||||
// ensure path segments doesn't have PathArguments, only the last
|
||||
// segment is allowed to have one.
|
||||
if path
|
||||
.segments
|
||||
.iter()
|
||||
.take(path.segments.len() - 1)
|
||||
.any(|seg| seg.arguments != PathArguments::None)
|
||||
{
|
||||
component_path_cannot_have_arguments!(path.span());
|
||||
}
|
||||
|
||||
// ensure last segment only have value of None or AngleBracketed
|
||||
if !matches!(
|
||||
path.segments.last().unwrap().arguments,
|
||||
PathArguments::None | PathArguments::AngleBracketed(_)
|
||||
) {
|
||||
invalid_component_path!(path.span());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Component {
|
||||
fn parse(stream: ParseStream) -> Result<Self> {
|
||||
let name = syn::Path::parse_mod_style(stream)?;
|
||||
let mut name = stream.parse::<syn::Path>()?;
|
||||
Component::validate_component_path(&name)?;
|
||||
|
||||
// extract the path arguments from the path into prop_gen_args
|
||||
let prop_gen_args = name.segments.last_mut().and_then(|seg| {
|
||||
if let PathArguments::AngleBracketed(args) = seg.arguments.clone() {
|
||||
seg.arguments = PathArguments::None;
|
||||
Some(args)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
let content: ParseBuffer;
|
||||
|
||||
@@ -65,6 +104,7 @@ impl Parse for Component {
|
||||
|
||||
Ok(Self {
|
||||
name,
|
||||
prop_gen_args,
|
||||
body,
|
||||
children,
|
||||
manual_props,
|
||||
@@ -75,6 +115,7 @@ impl Parse for Component {
|
||||
impl ToTokens for Component {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let name = &self.name;
|
||||
let prop_gen_args = &self.prop_gen_args;
|
||||
|
||||
let mut has_key = None;
|
||||
|
||||
@@ -102,7 +143,10 @@ impl ToTokens for Component {
|
||||
}}
|
||||
}
|
||||
None => {
|
||||
let mut toks = quote! { fc_to_builder(#name) };
|
||||
let mut toks = match prop_gen_args {
|
||||
Some(gen_args) => quote! { fc_to_builder #gen_args(#name) },
|
||||
None => quote! { fc_to_builder(#name) },
|
||||
};
|
||||
for field in &self.body {
|
||||
match field.name.to_string().as_str() {
|
||||
"key" => {
|
||||
|
||||
@@ -9,3 +9,18 @@ macro_rules! attr_after_element {
|
||||
return Err(Error::new($span, "expected element\n = help move the attribute above all the children and text elements"));
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! component_path_cannot_have_arguments {
|
||||
($span:expr) => {
|
||||
return Err(Error::new(
|
||||
$span,
|
||||
"expected a path without arguments\n = try remove the path arguments",
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! invalid_component_path {
|
||||
($span:expr) => {
|
||||
return Err(Error::new($span, "Invalid component path syntax"));
|
||||
};
|
||||
}
|
||||
+41
-31
@@ -4,7 +4,7 @@ use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
token, Expr, LitStr, Result, Token,
|
||||
token, Expr, LitStr, Result,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -38,39 +38,49 @@ impl Parse for BodyNode {
|
||||
return Ok(BodyNode::Text(stream.parse()?));
|
||||
}
|
||||
|
||||
// div {} -> el
|
||||
// Div {} -> comp
|
||||
if stream.peek(syn::Ident) && stream.peek2(token::Brace) {
|
||||
if stream
|
||||
.fork()
|
||||
.parse::<Ident>()?
|
||||
.to_string()
|
||||
.chars()
|
||||
.next()
|
||||
.unwrap()
|
||||
.is_ascii_uppercase()
|
||||
{
|
||||
return Ok(BodyNode::Component(stream.parse()?));
|
||||
} else {
|
||||
return Ok(BodyNode::Element(stream.parse::<Element>()?));
|
||||
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
|
||||
//
|
||||
// example:
|
||||
// div {}
|
||||
if let Some(ident) = path.get_ident() {
|
||||
if body_stream.peek(token::Brace)
|
||||
&& ident
|
||||
.to_string()
|
||||
.chars()
|
||||
.next()
|
||||
.unwrap()
|
||||
.is_ascii_lowercase()
|
||||
{
|
||||
return Ok(BodyNode::Element(stream.parse::<Element>()?));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// component() -> comp
|
||||
// ::component {} -> comp
|
||||
// ::component () -> comp
|
||||
if (stream.peek(syn::Ident) && stream.peek2(token::Paren))
|
||||
|| (stream.peek(Token![::]))
|
||||
|| (stream.peek(Token![:]) && stream.peek2(Token![:]))
|
||||
{
|
||||
return Ok(BodyNode::Component(stream.parse::<Component>()?));
|
||||
}
|
||||
// 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 ()
|
||||
// ::Div {}
|
||||
// crate::Div {}
|
||||
// component()
|
||||
// ::component {}
|
||||
// ::component ()
|
||||
// crate::component{}
|
||||
// crate::component()
|
||||
// Input::<InputProps<'_, i32> {}
|
||||
// crate::Input::<InputProps<'_, i32> {}
|
||||
if body_stream.peek(token::Brace) || body_stream.peek(token::Paren) {
|
||||
Component::validate_component_path(&path)?;
|
||||
|
||||
// crate::component{} -> comp
|
||||
// crate::component() -> comp
|
||||
if let Ok(pat) = stream.fork().parse::<syn::Path>() {
|
||||
if pat.segments.len() > 1 {
|
||||
return Ok(BodyNode::Component(stream.parse::<Component>()?));
|
||||
return Ok(BodyNode::Component(stream.parse()?));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user