improve macro ergonomics

This commit is contained in:
Evan Almloff committed 2023-05-23 17:32:40 -05:00
1 parent 1f68399e7b
commit 502d670dff
4 files changed
+32 -53

No files matched your search

-6
View File
@@ -9,7 +9,6 @@ pub struct LayoutId(pub usize);
#[derive(Debug)] #[derive(Debug)]
pub struct Layout { pub struct Layout {
pub layout_name: Ident,
pub comp: Ident, pub comp: Ident,
pub props_name: Ident, pub props_name: Ident,
pub active_nests: Vec<NestId>, pub active_nests: Vec<NestId>,
@@ -37,10 +36,6 @@ impl Layout {
impl Layout { impl Layout {
pub fn parse(input: syn::parse::ParseStream, active_nests: Vec<NestId>) -> syn::Result<Self> { pub fn parse(input: syn::parse::ParseStream, active_nests: Vec<NestId>) -> syn::Result<Self> {
// Then parse the layout name
let _ = input.parse::<syn::Token![,]>();
let layout_name: syn::Ident = input.parse()?;
// Then parse the component name // Then parse the component name
let _ = input.parse::<syn::Token![,]>(); let _ = input.parse::<syn::Token![,]>();
let comp: Ident = input.parse()?; let comp: Ident = input.parse()?;
@@ -52,7 +47,6 @@ impl Layout {
.unwrap_or_else(|_| format_ident!("{}Props", comp.to_string())); .unwrap_or_else(|_| format_ident!("{}Props", comp.to_string()));
Ok(Self { Ok(Self {
layout_name,
comp, comp,
props_name, props_name,
active_nests, active_nests,
+28 -23
View File
@@ -5,7 +5,7 @@ use nest::{Nest, NestId};
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::{__private::Span, format_ident, quote, ToTokens}; use quote::{__private::Span, format_ident, quote, ToTokens};
use route::Route; use route::Route;
use syn::{parse::ParseStream, parse_macro_input, Ident}; use syn::{parse::ParseStream, parse_macro_input, Ident, Token};
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
@@ -18,9 +18,8 @@ mod route;
mod route_tree; mod route_tree;
mod segment; mod segment;
// #[proc_macro_derive(Routable, attributes(route, nest, end_nest))] #[proc_macro_derive(Routable, attributes(route, nest, end_nest, layout, end_layout))]
#[proc_macro_attribute] pub fn routable(input: TokenStream) -> TokenStream {
pub fn routable(_: TokenStream, input: TokenStream) -> TokenStream {
let routes_enum = parse_macro_input!(input as syn::ItemEnum); let routes_enum = parse_macro_input!(input as syn::ItemEnum);
let route_enum = match RouteEnum::parse(routes_enum) { let route_enum = match RouteEnum::parse(routes_enum) {
@@ -48,8 +47,6 @@ pub fn routable(_: TokenStream, input: TokenStream) -> TokenStream {
} }
struct RouteEnum { struct RouteEnum {
attrs: Vec<syn::Attribute>,
vis: syn::Visibility,
name: Ident, name: Ident,
routes: Vec<Route>, routes: Vec<Route>,
nests: Vec<Nest>, nests: Vec<Nest>,
@@ -62,13 +59,14 @@ impl RouteEnum {
let mut routes = Vec::new(); let mut routes = Vec::new();
let mut layouts = Vec::new(); let mut layouts: Vec<Layout> = Vec::new();
let mut layout_stack = Vec::new(); let mut layout_stack = Vec::new();
let mut nests = Vec::new(); let mut nests = Vec::new();
let mut nest_stack = Vec::new(); let mut nest_stack = Vec::new();
for variant in &data.variants { for variant in &data.variants {
let mut excluded = Vec::new();
// Apply the any nesting attributes in order // Apply the any nesting attributes in order
for attr in &variant.attrs { for attr in &variant.attrs {
if attr.path.is_ident("nest") { if attr.path.is_ident("nest") {
@@ -113,15 +111,31 @@ impl RouteEnum {
} else if attr.path.is_ident("end_nest") { } else if attr.path.is_ident("end_nest") {
nest_stack.pop(); nest_stack.pop();
} else if attr.path.is_ident("layout") { } else if attr.path.is_ident("layout") {
let layout_index = layouts.len();
let parser = |input: ParseStream| { let parser = |input: ParseStream| {
Layout::parse(input, nest_stack.iter().rev().cloned().collect()) let bang: Option<Token![!]> = input.parse().ok();
let exclude = bang.is_some();
Ok((
exclude,
Layout::parse(input, nest_stack.iter().rev().cloned().collect())?,
))
}; };
let layout = attr.parse_args_with(parser)?; let (exclude, layout): (bool, Layout) = attr.parse_args_with(parser)?;
layouts.push(layout); if exclude {
layout_stack.push(LayoutId(layout_index)); let Some(layout_index) =
layouts.iter().position(|l| l.comp == layout.comp)else{
return Err(syn::Error::new(
Span::call_site(),
"Attempted to exclude a layout that does not exist",
));
}
;
excluded.push(LayoutId(layout_index));
} else {
let layout_index = layouts.len();
layouts.push(layout);
layout_stack.push(LayoutId(layout_index));
}
} else if attr.path.is_ident("end_layout") { } else if attr.path.is_ident("end_layout") {
layout_stack.pop(); layout_stack.pop();
} }
@@ -130,6 +144,7 @@ impl RouteEnum {
let mut active_nests = nest_stack.clone(); let mut active_nests = nest_stack.clone();
active_nests.reverse(); active_nests.reverse();
let mut active_layouts = layout_stack.clone(); let mut active_layouts = layout_stack.clone();
active_layouts.retain(|&id| !excluded.contains(&id));
active_layouts.reverse(); active_layouts.reverse();
let route = Route::parse(active_nests, active_layouts, variant.clone())?; let route = Route::parse(active_nests, active_layouts, variant.clone())?;
@@ -139,8 +154,6 @@ impl RouteEnum {
let myself = Self { let myself = Self {
name: name.clone(), name: name.clone(),
attrs: data.attrs,
vis: data.vis,
routes, routes,
nests, nests,
layouts, layouts,
@@ -315,16 +328,8 @@ impl RouteEnum {
impl ToTokens for RouteEnum { impl ToTokens for RouteEnum {
fn to_tokens(&self, tokens: &mut quote::__private::TokenStream) { fn to_tokens(&self, tokens: &mut quote::__private::TokenStream) {
let routes = &self.routes; let routes = &self.routes;
let vis = &self.vis;
let name = &self.name;
let attrs = &self.attrs;
let variants = routes.iter().map(|r| r.variant());
tokens.extend(quote!( tokens.extend(quote!(
#(#attrs)*
#vis enum #name {
#(#variants),*
}
#[path = "pages"] #[path = "pages"]
mod pages { mod pages {
+3 -23
View File
@@ -186,13 +186,10 @@ impl Route {
} }
} }
for segment in &self.segments { for segment in &self.segments {
match segment { if let RouteSegment::Dynamic(name, ..) = segment {
RouteSegment::Dynamic(name, _) => { if name == f.ident.as_ref().unwrap() {
if name == f.ident.as_ref().unwrap() { from_route = true
from_route = true
}
} }
_ => {}
} }
} }
@@ -273,23 +270,6 @@ impl Route {
} }
} }
pub fn variant(&self) -> TokenStream2 {
let name = &self.route_name;
let fields = self.fields.named.iter().map(|f| {
let mut new = f.clone();
new.attrs.retain(|a| {
!a.path.is_ident("nest")
&& !a.path.is_ident("end_nest")
&& !a.path.is_ident("layout")
&& !a.path.is_ident("end_layout")
});
new
});
quote! {
#name { #(#fields,)* }
}
}
} }
impl ToTokens for Route { impl ToTokens for Route {
+1 -1
View File
@@ -48,7 +48,7 @@ pub mod prelude {
pub use crate::contexts::*; pub use crate::contexts::*;
pub use crate::hooks::*; pub use crate::hooks::*;
pub use crate::router_cfg::RouterConfiguration; pub use crate::router_cfg::RouterConfiguration;
pub use dioxus_router_macro::routable; pub use dioxus_router_macro::Routable;
} }
mod utils { mod utils {