Browse Source

seperate dynamic nested segments and layouts

Evan Almloff 2 years ago
parent
commit
ef6551a6cd

+ 61 - 0
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<NestId>,
+}
+
+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<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
+        let _ = input.parse::<syn::Token![,]>();
+        let comp: Ident = input.parse()?;
+
+        // Then parse the props name
+        let _ = input.parse::<syn::Token![,]>();
+        let props_name: Ident = input
+            .parse()
+            .unwrap_or_else(|_| format_ident!("{}Props", comp.to_string()));
+
+        Ok(Self {
+            layout_name,
+            comp,
+            props_name,
+            active_nests,
+        })
+    }
+}

+ 79 - 67
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<syn::Attribute>,
+    vis: syn::Visibility,
     name: Ident,
     routes: Vec<Route>,
+    nests: Vec<Nest>,
     layouts: Vec<Layout>,
 }
 
@@ -57,79 +60,89 @@ impl RouteEnum {
     fn parse(data: syn::ItemEnum) -> syn::Result<Self> {
         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::<Vec<_>>();
-                            // 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::<Vec<_>>();
-            trailing_static_route.reverse();
-            let active_layouts = nest_stack
-                .iter()
-                .filter_map(|nest| match nest {
-                    NestRef::Static(_) => None,
-                    NestRef::Dynamic { id } => Some(*id),
-                })
-                .collect::<Vec<_>>();
-
-            let route = Route::parse(trailing_static_route.join("/"), active_layouts, variant)?;
+            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())?;
+
             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;
-
-            let error_name = layout.error_ident();
-            let route_str = &layout.route;
+        for nest in &self.nests {
+            let error_variant = nest.error_variant();
+            let error_name = nest.error_ident();
+            let route_str = &nest.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)*

+ 44 - 94
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);
+
+#[derive(Debug, Clone)]
+pub struct Nest {
+    pub route: String,
+    pub segments: Vec<RouteSegment>,
+    index: usize,
 }
 
-impl Parse for Nest {
-    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
-        // First parse the route
+impl Nest {
+    pub fn parse(
+        input: syn::parse::ParseStream,
+        children_routes: Vec<syn::FieldsNamed>,
+        index: usize,
+    ) -> syn::Result<Self> {
+        // 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::<syn::Token![,]>();
-            let layout_name: syn::Ident = input.parse()?;
-            let layout_fields: syn::FieldsNamed = input.parse()?;
-
-            // Then parse the component name
-            let _ = input.parse::<syn::Token![,]>();
-            let comp: Ident = input.parse()?;
-
-            // Then parse the props name
-            let _ = input.parse::<syn::Token![,]>();
-            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()))
+        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()
+                    ),
+                ));
+            }
         }
-    }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct LayoutId(pub usize);
 
-#[derive(Debug)]
-pub struct Layout {
-    pub route: String,
-    pub segments: Vec<RouteSegment>,
-    pub layout_name: Ident,
-    pub layout_fields: syn::FieldsNamed,
-    pub comp: Ident,
-    pub props_name: Ident,
-}
-
-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()));
+        Ok(Self {
+            route: route.value(),
+            segments: route_segments,
+            index,
+        })
     }
+}
 
+impl Nest {
     pub fn dynamic_segments(&self) -> impl Iterator<Item = TokenStream> + '_ {
         self.segments
             .iter()
@@ -85,13 +57,6 @@ impl Layout {
             .map(|i| quote! {#i})
     }
 
-    pub fn dynamic_segment_types(&self) -> impl Iterator<Item = TokenStream> + '_ {
-        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)
-        }
-    }
 }

+ 0 - 8
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
-    }
 }

+ 43 - 80
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<RouteSegment>,
     pub query: Option<QuerySegment>,
+    pub nests: Vec<NestId>,
     pub layouts: Vec<LayoutId>,
     pub variant: syn::Variant,
+    fields: syn::FieldsNamed,
 }
 
 impl Route {
     pub fn parse(
-        root_route: String,
+        nests: Vec<NestId>,
         layouts: Vec<LayoutId>,
         variant: syn::Variant,
     ) -> syn::Result<Self> {
@@ -67,7 +71,7 @@ impl Route {
 
         let route_name = variant.ident.clone();
         let args = route_attr.parse_args::<RouteArgs>()?;
-        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<TokenStream2> {
+    pub fn routable_match(
+        &self,
+        layouts: &[Layout],
+        nests: &[Nest],
+        index: usize,
+    ) -> Option<TokenStream2> {
         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<Item = TokenStream2> + '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_from_route(&self) -> impl Iterator<Item = TokenStream2> + '_ {
-        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<Item = TokenStream2> + '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)
+    fn dynamic_segments(&self) -> impl Iterator<Item = TokenStream2> + '_ {
+        self.fields.named.iter().map(|f| {
+            let name = f.ident.as_ref().unwrap();
+            quote! {#name}
+        })
     }
 
-    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,)* }
         }
     }
 }

+ 50 - 50
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<usize> {
         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::<Vec<_>>();
 
         let mut myself = Self::default();
@@ -109,11 +109,11 @@ impl<'a> RouteTree<'a> {
         for mut route in routes {
             let mut current_route: Option<usize> = None;
 
-            // First add a layout if there is one
-            while let Some(layout) = route.next_layout() {
-                let segments_iter: std::slice::Iter<RouteSegment> = 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<usize>,
     },
-    Layout {
-        layout: &'a Layout,
+    Nest {
+        nest: &'a Nest,
         children: Vec<usize>,
     },
     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)> {

+ 16 - 30
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<Item = &'a syn::Field>,
     route: &str,
 ) -> syn::Result<(Vec<RouteSegment>, Option<QuerySegment>)> {
     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),
                     ));
                 };