diff --git a/packages/router-macro/src/layout.rs b/packages/router-macro/src/layout.rs new file mode 100644 index 000000000..631aacedc --- /dev/null +++ b/packages/router-macro/src/layout.rs @@ -0,0 +1,61 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::Ident; + +use crate::nest::{Nest, NestId}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct LayoutId(pub usize); + +#[derive(Debug)] +pub struct Layout { + pub layout_name: Ident, + pub comp: Ident, + pub props_name: Ident, + pub active_nests: Vec, +} + +impl Layout { + pub fn routable_match(&self, nests: &[Nest]) -> TokenStream { + let props_name = &self.props_name; + let comp_name = &self.comp; + let dynamic_segments = self + .active_nests + .iter() + .flat_map(|id| nests[id.0].dynamic_segments()); + + quote! { + let comp = #props_name { #(#dynamic_segments,)* }; + let cx = cx.bump().alloc(Scoped { + props: cx.bump().alloc(comp), + scope: cx, + }); + #comp_name(cx) + } + } +} + +impl Layout { + pub fn parse(input: syn::parse::ParseStream, active_nests: Vec) -> syn::Result { + // Then parse the layout name + let _ = input.parse::(); + let layout_name: syn::Ident = input.parse()?; + + // Then parse the component name + let _ = input.parse::(); + let comp: Ident = input.parse()?; + + // Then parse the props name + let _ = input.parse::(); + let props_name: Ident = input + .parse() + .unwrap_or_else(|_| format_ident!("{}Props", comp.to_string())); + + Ok(Self { + layout_name, + comp, + props_name, + active_nests, + }) + } +} diff --git a/packages/router-macro/src/lib.rs b/packages/router-macro/src/lib.rs index 270dad986..25a510f50 100644 --- a/packages/router-macro/src/lib.rs +++ b/packages/router-macro/src/lib.rs @@ -1,15 +1,17 @@ extern crate proc_macro; -use nest::{Layout, Nest}; +use layout::Layout; +use nest::{Nest, NestId}; use proc_macro::TokenStream; use quote::{__private::Span, format_ident, quote, ToTokens}; use route::Route; -use syn::{parse_macro_input, Ident}; +use syn::{parse::ParseStream, parse_macro_input, Ident}; use proc_macro2::TokenStream as TokenStream2; -use crate::{nest::LayoutId, route_tree::RouteTree}; +use crate::{layout::LayoutId, route_tree::RouteTree}; +mod layout; mod nest; mod query; mod route; @@ -46,10 +48,11 @@ pub fn routable(_: TokenStream, input: TokenStream) -> TokenStream { } struct RouteEnum { - vis: syn::Visibility, attrs: Vec, + vis: syn::Visibility, name: Ident, routes: Vec, + nests: Vec, layouts: Vec, } @@ -57,79 +60,89 @@ impl RouteEnum { fn parse(data: syn::ItemEnum) -> syn::Result { let name = &data.ident; - enum NestRef { - Static(String), - Dynamic { id: LayoutId }, - } - let mut routes = Vec::new(); let mut layouts = Vec::new(); + let mut layout_stack = Vec::new(); + let mut nests = Vec::new(); let mut nest_stack = Vec::new(); - for variant in data.variants { + for variant in &data.variants { // Apply the any nesting attributes in order for attr in &variant.attrs { if attr.path.is_ident("nest") { - let nest: Nest = attr.parse_args()?; - let nest_ref = match nest { - Nest::Static(s) => NestRef::Static(s), - Nest::Layout(mut l) => { - // if there is a static nest before this, add it to the layout - let mut static_prefix = nest_stack - .iter() - // walk backwards and take all static nests - .rev() - .map_while(|nest| match nest { - NestRef::Static(s) => Some(s.clone()), - NestRef::Dynamic { .. } => None, - }) - .collect::>(); - // reverse the static prefix so it is in the correct order - static_prefix.reverse(); - - if !static_prefix.is_empty() { - l.add_static_prefix(&static_prefix.join("/")); + let mut children_routes = Vec::new(); + { + // add all of the variants of the enum to the children_routes until we hit an end_nest + let mut level = 0; + 'o: for variant in &data.variants { + children_routes.push(variant.fields.clone()); + for attr in &variant.attrs { + if attr.path.is_ident("nest") { + level += 1; + } else if attr.path.is_ident("end_nest") { + level -= 1; + if level < 0 { + break 'o; + } + } } - - let id = layouts.len(); - layouts.push(l); - NestRef::Dynamic { id: LayoutId(id) } } + } + + let nest_index = nests.len(); + + let parser = |input: ParseStream| { + Nest::parse( + input, + children_routes + .iter() + .filter_map(|f: &syn::Fields| match f { + syn::Fields::Named(fields) => Some(fields.clone()), + _ => None, + }) + .collect(), + nest_index, + ) }; - nest_stack.push(nest_ref); + let nest = attr.parse_args_with(parser)?; + + nests.push(nest); + nest_stack.push(NestId(nest_index)); } else if attr.path.is_ident("end_nest") { nest_stack.pop(); + } else if attr.path.is_ident("layout") { + let layout_index = layouts.len(); + + let parser = |input: ParseStream| { + Layout::parse(input, nest_stack.iter().rev().cloned().collect()) + }; + let layout = attr.parse_args_with(parser)?; + + layouts.push(layout); + layout_stack.push(LayoutId(layout_index)); + } else if attr.path.is_ident("end_layout") { + layout_stack.pop(); } } - let mut trailing_static_route = nest_stack - .iter() - .rev() - .map_while(|nest| match nest { - NestRef::Static(s) => Some(s.clone()), - NestRef::Dynamic { .. } => None, - }) - .collect::>(); - trailing_static_route.reverse(); - let active_layouts = nest_stack - .iter() - .filter_map(|nest| match nest { - NestRef::Static(_) => None, - NestRef::Dynamic { id } => Some(*id), - }) - .collect::>(); + let mut active_nests = nest_stack.clone(); + active_nests.reverse(); + let mut active_layouts = layout_stack.clone(); + active_layouts.reverse(); + + let route = Route::parse(active_nests, active_layouts, variant.clone())?; - let route = Route::parse(trailing_static_route.join("/"), active_layouts, variant)?; routes.push(route); } let myself = Self { - vis: data.vis, - attrs: data.attrs, name: name.clone(), + attrs: data.attrs, + vis: data.vis, routes, + nests, layouts, }; @@ -140,7 +153,7 @@ impl RouteEnum { let mut display_match = Vec::new(); for route in &self.routes { - display_match.push(route.display_match(&self.layouts)); + display_match.push(route.display_match(&self.nests)); } let name = &self.name; @@ -158,13 +171,13 @@ impl RouteEnum { } fn parse_impl(&self) -> TokenStream2 { - let tree = RouteTree::new(&self.routes, &self.layouts); + let tree = RouteTree::new(&self.routes, &self.nests); let name = &self.name; let error_name = format_ident!("{}MatchError", self.name); let tokens = tree.roots.iter().map(|&id| { let route = tree.get(id).unwrap(); - route.to_tokens(&tree, self.name.clone(), error_name.clone(), &self.layouts) + route.to_tokens(&tree, self.name.clone(), error_name.clone()) }); quote! { @@ -217,15 +230,14 @@ impl RouteEnum { type_defs.push(route.error_type()); } - for layout in &self.layouts { - let layout_name = &layout.layout_name; + for nest in &self.nests { + let error_variant = nest.error_variant(); + let error_name = nest.error_ident(); + let route_str = &nest.route; - let error_name = layout.error_ident(); - let route_str = &layout.route; - - error_variants.push(quote! { #layout_name(#error_name) }); - display_match.push(quote! { Self::#layout_name(err) => write!(f, "Layout '{}' ('{}') did not match:\n{}", stringify!(#layout_name), #route_str, err)? }); - type_defs.push(layout.error_type()); + error_variants.push(quote! { #error_variant(#error_name) }); + display_match.push(quote! { Self::#error_variant(err) => write!(f, "Nest '{}' ('{}') did not match:\n{}", stringify!(#error_name), #route_str, err)? }); + type_defs.push(nest.error_type()); } quote! { @@ -259,7 +271,7 @@ impl RouteEnum { // Collect all routes that match the current layer for route in &self.routes { - if let Some(matched) = route.routable_match(&self.layouts, index) { + if let Some(matched) = route.routable_match(&self.layouts, &self.nests, index) { routable_match.push(matched); } } @@ -303,7 +315,7 @@ impl ToTokens for RouteEnum { let vis = &self.vis; let name = &self.name; let attrs = &self.attrs; - let variants = routes.iter().map(|r| r.variant(&self.layouts)); + let variants = routes.iter().map(|r| r.variant()); tokens.extend(quote!( #(#attrs)* diff --git a/packages/router-macro/src/nest.rs b/packages/router-macro/src/nest.rs index 1dbe1ace4..64b416863 100644 --- a/packages/router-macro/src/nest.rs +++ b/packages/router-macro/src/nest.rs @@ -1,83 +1,55 @@ use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use syn::{parse::Parse, Ident, LitStr}; +use syn::{Ident, LitStr}; use crate::segment::{parse_route_segments, RouteSegment}; -pub enum Nest { - Static(String), - Layout(Layout), -} +#[derive(Debug, Clone, Copy)] +pub struct NestId(pub usize); -impl Parse for Nest { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - // First parse the route - let route: LitStr = input.parse()?; - let is_dynamic = route.value().contains('('); - - if !input.is_empty() || is_dynamic { - // Then parse the layout name - let _ = input.parse::(); - let layout_name: syn::Ident = input.parse()?; - let layout_fields: syn::FieldsNamed = input.parse()?; - - // Then parse the component name - let _ = input.parse::(); - let comp: Ident = input.parse()?; - - // Then parse the props name - let _ = input.parse::(); - let props_name: Ident = input - .parse() - .unwrap_or_else(|_| format_ident!("{}Props", comp.to_string())); - - let route_segments = - parse_route_segments(&layout_name, &layout_fields, &route.value())?.0; - for seg in &route_segments { - if let RouteSegment::CatchAll(name, _) = seg { - return Err(syn::Error::new_spanned( - name, - format!( - "Catch-all segments are not allowed in nested routes: {}", - route.value() - ), - )); - } - } - - Ok(Self::Layout(Layout { - route: route.value(), - segments: route_segments, - layout_name, - comp, - props_name, - layout_fields, - })) - } else { - Ok(Self::Static(route.value())) - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct LayoutId(pub usize); - -#[derive(Debug)] -pub struct Layout { +#[derive(Debug, Clone)] +pub struct Nest { pub route: String, pub segments: Vec, - pub layout_name: Ident, - pub layout_fields: syn::FieldsNamed, - pub comp: Ident, - pub props_name: Ident, + index: usize, } -impl Layout { - pub fn add_static_prefix(&mut self, prefix: &str) { - self.route = format!("{}{}", prefix, self.route); - self.segments.push(RouteSegment::Static(prefix.to_string())); - } +impl Nest { + pub fn parse( + input: syn::parse::ParseStream, + children_routes: Vec, + index: usize, + ) -> syn::Result { + // Parse the route + let route: LitStr = input.parse()?; + let route_segments = parse_route_segments( + route.span(), + children_routes.iter().flat_map(|f| f.named.iter()), + &route.value(), + )? + .0; + for seg in &route_segments { + if let RouteSegment::CatchAll(name, _) = seg { + return Err(syn::Error::new_spanned( + name, + format!( + "Catch-all segments are not allowed in nested routes: {}", + route.value() + ), + )); + } + } + + Ok(Self { + route: route.value(), + segments: route_segments, + index, + }) + } +} + +impl Nest { pub fn dynamic_segments(&self) -> impl Iterator + '_ { self.segments .iter() @@ -85,13 +57,6 @@ impl Layout { .map(|i| quote! {#i}) } - pub fn dynamic_segment_types(&self) -> impl Iterator + '_ { - self.segments - .iter() - .filter_map(|seg| seg.ty()) - .map(|ty| quote! {#ty}) - } - pub fn write(&self) -> TokenStream { let write_segments = self.segments.iter().map(|s| s.write_segment()); @@ -103,7 +68,11 @@ impl Layout { } pub fn error_ident(&self) -> Ident { - format_ident!("{}LayoutParseError", self.layout_name) + format_ident!("Nest{}ParseError", self.index) + } + + pub fn error_variant(&self) -> Ident { + format_ident!("Nest{}", self.index) } pub fn error_type(&self) -> TokenStream { @@ -147,23 +116,4 @@ impl Layout { } } } - - pub fn routable_match(&self) -> TokenStream { - let props_name = &self.props_name; - let comp_name = &self.comp; - let dynamic_segments_from_route = self - .segments - .iter() - .filter_map(|seg| seg.name()) - .map(|seg| quote! { #seg }); - - quote! { - let comp = #props_name { #(#dynamic_segments_from_route,)* }; - let cx = cx.bump().alloc(Scoped { - props: cx.bump().alloc(comp), - scope: cx, - }); - #comp_name(cx) - } - } } diff --git a/packages/router-macro/src/query.rs b/packages/router-macro/src/query.rs index 87db30453..8306254da 100644 --- a/packages/router-macro/src/query.rs +++ b/packages/router-macro/src/query.rs @@ -24,12 +24,4 @@ impl QuerySegment { write!(f, "?{}", #ident)?; } } - - pub fn name(&self) -> Ident { - self.ident.clone() - } - - pub fn ty(&self) -> &Type { - &self.ty - } } diff --git a/packages/router-macro/src/route.rs b/packages/router-macro/src/route.rs index 804f89c92..466d23220 100644 --- a/packages/router-macro/src/route.rs +++ b/packages/router-macro/src/route.rs @@ -5,8 +5,10 @@ use syn::{Ident, LitStr}; use proc_macro2::TokenStream as TokenStream2; -use crate::nest::Layout; -use crate::nest::LayoutId; +use crate::layout::Layout; +use crate::layout::LayoutId; +use crate::nest::Nest; +use crate::nest::NestId; use crate::query::QuerySegment; use crate::segment::parse_route_segments; use crate::segment::RouteSegment; @@ -44,13 +46,15 @@ pub struct Route { pub route: String, pub segments: Vec, pub query: Option, + pub nests: Vec, pub layouts: Vec, pub variant: syn::Variant, + fields: syn::FieldsNamed, } impl Route { pub fn parse( - root_route: String, + nests: Vec, layouts: Vec, variant: syn::Variant, ) -> syn::Result { @@ -67,7 +71,7 @@ impl Route { let route_name = variant.ident.clone(); let args = route_attr.parse_args::()?; - let route = root_route + &args.route.value(); + let route = args.route.value(); let file_based = args.comp_name.is_none(); let comp_name = args .comp_name @@ -86,7 +90,8 @@ impl Route { } }; - let (route_segments, query) = parse_route_segments(&variant.ident, named_fields, &route)?; + let (route_segments, query) = + parse_route_segments(variant.ident.span(), named_fields.named.iter(), &route)?; Ok(Self { comp_name, @@ -96,15 +101,17 @@ impl Route { route, file_based, query, + nests, layouts, + fields: named_fields.clone(), variant, }) } - pub fn display_match(&self, layouts: &[Layout]) -> TokenStream2 { + pub fn display_match(&self, nests: &[Nest]) -> TokenStream2 { let name = &self.route_name; - let dynamic_segments = self.dynamic_segments(layouts); - let write_layouts = self.layouts.iter().map(|id| layouts[id.0].write()); + let dynamic_segments = self.dynamic_segments(); + let write_layouts = self.nests.iter().map(|id| nests[id.0].write()); let write_segments = self.segments.iter().map(|s| s.write_segment()); let write_query = self.query.as_ref().map(|q| q.write()); @@ -117,14 +124,19 @@ impl Route { } } - pub fn routable_match(&self, layouts: &[Layout], index: usize) -> Option { + pub fn routable_match( + &self, + layouts: &[Layout], + nests: &[Nest], + index: usize, + ) -> Option { let name = &self.route_name; - let dynamic_segments = self.dynamic_segments(layouts); + let dynamic_segments = self.dynamic_segments(); match index.cmp(&self.layouts.len()) { std::cmp::Ordering::Less => { let layout = self.layouts[index]; - let render_layout = layouts[layout.0].routable_match(); + let render_layout = layouts[layout.0].routable_match(nests); // This is a layout Some(quote! { #[allow(unused)] @@ -134,7 +146,7 @@ impl Route { }) } std::cmp::Ordering::Equal => { - let dynamic_segments_from_route = self.dynamic_segments_from_route(); + let dynamic_segments_from_route = self.dynamic_segments(); let props_name = &self.props_name; let comp_name = &self.comp_name; // This is the final route @@ -154,72 +166,15 @@ impl Route { } } - fn dynamic_segment_types<'a>( - &'a self, - layouts: &'a [Layout], - ) -> impl Iterator + 'a { - let layouts = self - .layouts - .iter() - .flat_map(|id| layouts[id.0].dynamic_segment_types()); - let segments = self.segments.iter().filter_map(|seg| { - let ty = seg.ty()?; - - Some(quote! { - #ty - }) - }); - let query = self - .query - .as_ref() - .map(|q| { - let ty = q.ty(); - quote! { - #ty - } - }) - .into_iter(); - - layouts.chain(segments.chain(query)) + fn dynamic_segments(&self) -> impl Iterator + '_ { + self.fields.named.iter().map(|f| { + let name = f.ident.as_ref().unwrap(); + quote! {#name} + }) } - fn dynamic_segments_from_route(&self) -> impl Iterator + '_ { - let segments = self.segments.iter().filter_map(|seg| { - seg.name().map(|name| { - quote! { - #name - } - }) - }); - let query = self - .query - .as_ref() - .map(|q| { - let name = q.name(); - quote! { - #name - } - }) - .into_iter(); - - segments.chain(query) - } - - fn dynamic_segments<'a>( - &'a self, - layouts: &'a [Layout], - ) -> impl Iterator + 'a { - let layouts = self - .layouts - .iter() - .flat_map(|id| layouts[id.0].dynamic_segments()); - let dynamic_segments = self.dynamic_segments_from_route(); - - layouts.chain(dynamic_segments) - } - - pub fn construct(&self, enum_name: Ident, layouts: &[Layout]) -> TokenStream2 { - let segments = self.dynamic_segments(layouts); + pub fn construct(&self, enum_name: Ident) -> TokenStream2 { + let segments = self.dynamic_segments(); let name = &self.route_name; quote! { @@ -289,13 +244,21 @@ impl Route { } } - pub fn variant(&self, layouts: &[Layout]) -> TokenStream2 { + pub fn variant(&self) -> TokenStream2 { let name = &self.route_name; - let segments = self.dynamic_segments(layouts); - let types = self.dynamic_segment_types(layouts); + 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 { #(#segments: #types,)* } + #name { #(#fields,)* } } } } diff --git a/packages/router-macro/src/route_tree.rs b/packages/router-macro/src/route_tree.rs index 3e91dc482..c017d1433 100644 --- a/packages/router-macro/src/route_tree.rs +++ b/packages/router-macro/src/route_tree.rs @@ -4,7 +4,7 @@ use slab::Slab; use syn::Ident; use crate::{ - nest::Layout, + nest::Nest, route::Route, segment::{static_segment_idx, RouteSegment}, }; @@ -39,7 +39,7 @@ impl<'a> RouteTree<'a> { let seg = self.get(seg).unwrap(); match seg { RouteTreeSegmentData::Static { .. } => 0, - RouteTreeSegmentData::Layout { .. } => 1, + RouteTreeSegmentData::Nest { .. } => 1, RouteTreeSegmentData::Route(route) => { // Routes that end in a catch all segment should be checked last match route.segments.last() { @@ -70,7 +70,7 @@ impl<'a> RouteTree<'a> { let element = self.entries.get(element).unwrap(); match element { RouteTreeSegmentData::Static { children, .. } => children.clone(), - RouteTreeSegmentData::Layout { children, .. } => children.clone(), + RouteTreeSegmentData::Nest { children, .. } => children.clone(), _ => Vec::new(), } } @@ -79,20 +79,20 @@ impl<'a> RouteTree<'a> { let element = self.entries.get_mut(element).unwrap(); match element { RouteTreeSegmentData::Static { children, .. } => Some(children), - RouteTreeSegmentData::Layout { children, .. } => Some(children), + RouteTreeSegmentData::Nest { children, .. } => Some(children), _ => None, } } fn children_mut(&mut self, element: usize) -> &mut Vec { self.try_children_mut(element) - .expect("Cannot get children of non static or layout segment") + .expect("Cannot get children of non static or nest segment") } - pub fn new(routes: &'a [Route], layouts: &'a [Layout]) -> Self { + pub fn new(routes: &'a [Route], nests: &'a [Nest]) -> Self { let routes = routes .iter() - .map(|route| RouteIter::new(route, layouts)) + .map(|route| RouteIter::new(route, nests)) .collect::>(); let mut myself = Self::default(); @@ -109,11 +109,11 @@ impl<'a> RouteTree<'a> { for mut route in routes { let mut current_route: Option = None; - // First add a layout if there is one - while let Some(layout) = route.next_layout() { - let segments_iter: std::slice::Iter = layout.segments.iter(); + // First add all nests + while let Some(nest) = route.next_nest() { + let segments_iter = nest.segments.iter(); - // Add all static segments of the layout + // Add all static segments of the nest 'o: for (index, segment) in segments_iter.enumerate() { match segment { RouteSegment::Static(segment) => { @@ -124,17 +124,12 @@ impl<'a> RouteTree<'a> { .map(|id| self.children(id)) .unwrap_or_else(|| segments.clone()); - for seg in segments.iter() { - let seg = self.get(*seg).unwrap(); - if let RouteTreeSegmentData::Static { - segment: s, - children, - .. - } = seg - { + for &seg_id in segments.iter() { + let seg = self.get(seg_id).unwrap(); + if let RouteTreeSegmentData::Static { segment: s, .. } = seg { if s == segment { // If it does, just update the current route - current_route = children.last().cloned(); + current_route = Some(seg_id); continue 'o; } } @@ -144,7 +139,10 @@ impl<'a> RouteTree<'a> { let static_segment = RouteTreeSegmentData::Static { segment, children: Vec::new(), - error_variant: route.error_variant(), + error_variant: StaticErrorVariant { + varient_parse_error: nest.error_ident(), + enum_varient: nest.error_variant(), + }, index, }; @@ -155,28 +153,31 @@ impl<'a> RouteTree<'a> { .map(|id| self.children_mut(id)) .unwrap_or_else(|| &mut segments); current_children.push(static_segment); + + // Update the current route + current_route = Some(static_segment); } // If there is a dynamic segment, stop adding static segments RouteSegment::Dynamic(..) => break, RouteSegment::CatchAll(..) => { - todo!("Catch all segments are not allowed in layouts") + todo!("Catch all segments are not allowed in nests") } } } - // Add the layout to the current route - let layout = RouteTreeSegmentData::Layout { - layout, + // Add the nest to the current route + let nest = RouteTreeSegmentData::Nest { + nest, children: Vec::new(), }; - let layout = self.entries.insert(layout); + let nest = self.entries.insert(nest); let segments = match current_route.and_then(|id| self.get_mut(id)) { Some(RouteTreeSegmentData::Static { children, .. }) => children, - Some(_) => unreachable!(), + Some(r) => unreachable!("{r:?} is not a static segment"), None => &mut segments, }; - segments.push(layout); + segments.push(nest); // Update the current route current_route = segments.last().cloned(); @@ -252,8 +253,8 @@ pub enum RouteTreeSegmentData<'a> { index: usize, children: Vec, }, - Layout { - layout: &'a Layout, + Nest { + nest: &'a Nest, children: Vec, }, Route(&'a Route), @@ -265,7 +266,6 @@ impl<'a> RouteTreeSegmentData<'a> { tree: &RouteTree, enum_name: syn::Ident, error_enum_name: syn::Ident, - layouts: &[Layout], ) -> TokenStream { match self { RouteTreeSegmentData::Static { @@ -282,7 +282,7 @@ impl<'a> RouteTreeSegmentData<'a> { let children = children.iter().map(|child| { let child = tree.get(*child).unwrap(); - child.to_tokens(tree, enum_name.clone(), error_enum_name.clone(), layouts) + child.to_tokens(tree, enum_name.clone(), error_enum_name.clone()) }); quote! { @@ -310,7 +310,7 @@ impl<'a> RouteTreeSegmentData<'a> { .enumerate() .skip_while(|(_, seg)| matches!(seg, RouteSegment::Static(_))); - let construct_variant = route.construct(enum_name, layouts); + let construct_variant = route.construct(enum_name); let parse_query = route.parse_query(); let insure_not_trailing = route @@ -334,12 +334,12 @@ impl<'a> RouteTreeSegmentData<'a> { &varient_parse_error, ) } - Self::Layout { layout, children } => { + Self::Nest { nest, children } => { // At this point, we have matched all static segments, so we can just check if the remaining segments match the route - let varient_parse_error: Ident = layout.error_ident(); - let enum_varient = &layout.layout_name; + let varient_parse_error: Ident = nest.error_ident(); + let enum_varient = nest.error_variant(); - let route_segments = layout + let route_segments = nest .segments .iter() .enumerate() @@ -349,7 +349,7 @@ impl<'a> RouteTreeSegmentData<'a> { .iter() .map(|child| { let child = tree.get(*child).unwrap(); - child.to_tokens(tree, enum_name.clone(), error_enum_name.clone(), layouts) + child.to_tokens(tree, enum_name.clone(), error_enum_name.clone()) }) .collect(); @@ -357,7 +357,7 @@ impl<'a> RouteTreeSegmentData<'a> { route_segments.peekable(), parse_children, &error_enum_name, - enum_varient, + &enum_varient, &varient_parse_error, ) } @@ -436,27 +436,27 @@ fn return_constructed( pub struct RouteIter<'a> { route: &'a Route, - layouts: &'a [Layout], - layout_index: usize, + nests: &'a [Nest], + nest_index: usize, static_segment_index: usize, } impl<'a> RouteIter<'a> { - fn new(route: &'a Route, layouts: &'a [Layout]) -> Self { + fn new(route: &'a Route, nests: &'a [Nest]) -> Self { Self { route, - layouts, - layout_index: 0, + nests, + nest_index: 0, static_segment_index: 0, } } - fn next_layout(&mut self) -> Option<&'a Layout> { - let idx = self.layout_index; - let layout_index = self.route.layouts.get(idx)?; - let layout = &self.layouts[layout_index.0]; - self.layout_index += 1; - Some(layout) + fn next_nest(&mut self) -> Option<&'a Nest> { + let idx = self.nest_index; + let nest_index = self.route.nests.get(idx)?; + let nest = &self.nests[nest_index.0]; + self.nest_index += 1; + Some(nest) } fn next_static_segment(&mut self) -> Option<(usize, &'a str)> { diff --git a/packages/router-macro/src/segment.rs b/packages/router-macro/src/segment.rs index 6db846cea..e4e786ed6 100644 --- a/packages/router-macro/src/segment.rs +++ b/packages/router-macro/src/segment.rs @@ -5,7 +5,7 @@ use proc_macro2::{Span, TokenStream as TokenStream2}; use crate::query::QuerySegment; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum RouteSegment { Static(String), Dynamic(Ident, Type), @@ -21,14 +21,6 @@ impl RouteSegment { } } - pub fn ty(&self) -> Option<&Type> { - match self { - Self::Static(_) => None, - Self::Dynamic(_, ty) => Some(ty), - Self::CatchAll(_, ty) => Some(ty), - } - } - pub fn write_segment(&self) -> TokenStream2 { match self { Self::Static(segment) => quote! { write!(f, "/{}", #segment)?; }, @@ -130,9 +122,9 @@ pub fn static_segment_idx(idx: usize) -> Ident { format_ident!("StaticSegment{}ParseError", idx) } -pub fn parse_route_segments( - route_name: &Ident, - fields: &syn::FieldsNamed, +pub fn parse_route_segments<'a>( + route_span: Span, + mut fields: impl Iterator, route: &str, ) -> syn::Result<(Vec, Option)> { let mut route_segments = Vec::new(); @@ -146,8 +138,8 @@ pub fn parse_route_segments( // skip the first empty segment let first = iterator.next(); if first != Some("") { - return Err(syn::Error::new_spanned( - route_name, + return Err(syn::Error::new( + route_span, format!( "Routes should start with /. Error found in the route '{}'", route @@ -165,7 +157,7 @@ pub fn parse_route_segments( segment.to_string() }; - let field = fields.named.iter().find(|field| match field.ident { + let field = fields.find(|field| match field.ident { Some(ref field_ident) => *field_ident == ident, None => false, }); @@ -173,12 +165,9 @@ pub fn parse_route_segments( let ty = if let Some(field) = field { field.ty.clone() } else { - return Err(syn::Error::new_spanned( - route_name, - format!( - "Could not find a field with the name '{}' in the variant '{}'", - ident, route_name - ), + return Err(syn::Error::new( + route_span, + format!("Could not find a field with the name '{}'", ident,), )); }; if spread { @@ -188,8 +177,8 @@ pub fn parse_route_segments( )); if iterator.next().is_some() { - return Err(syn::Error::new_spanned( - route, + return Err(syn::Error::new( + route_span, "Catch-all route segments must be the last segment in a route. The route segments after the catch-all segment will never be matched.", )); } else { @@ -211,7 +200,7 @@ pub fn parse_route_segments( Some(query) => { if let Some(query) = query.strip_prefix(':') { let query_ident = Ident::new(query, Span::call_site()); - let field = fields.named.iter().find(|field| match field.ident { + let field = fields.find(|field| match field.ident { Some(ref field_ident) => field_ident == &query_ident, None => false, }); @@ -219,12 +208,9 @@ pub fn parse_route_segments( let ty = if let Some(field) = field { field.ty.clone() } else { - return Err(syn::Error::new_spanned( - route_name, - format!( - "Could not find a field with the name '{}' in the variant '{}'", - query_ident, route_name - ), + return Err(syn::Error::new( + route_span, + format!("Could not find a field with the name '{}'", query_ident), )); };