瀏覽代碼

fix reversed nests

Evan Almloff 1 年之前
父節點
當前提交
d19a33d59c

+ 3 - 11
packages/router-macro/src/lib.rs

@@ -364,10 +364,7 @@ impl RouteEnum {
                     let parser = |input: ParseStream| {
                         let bang: Option<Token![!]> = input.parse().ok();
                         let exclude = bang.is_some();
-                        Ok((
-                            exclude,
-                            Layout::parse(input, nest_stack.iter().rev().cloned().collect())?,
-                        ))
+                        Ok((exclude, Layout::parse(input, nest_stack.clone())?))
                     };
                     let (exclude, layout): (bool, Layout) = attr.parse_args_with(parser)?;
 
@@ -389,19 +386,14 @@ impl RouteEnum {
                     layout_stack.pop();
                 } else if attr.path.is_ident("redirect") {
                     let parser = |input: ParseStream| {
-                        Redirect::parse(
-                            input,
-                            nest_stack.iter().rev().cloned().collect(),
-                            redirects.len(),
-                        )
+                        Redirect::parse(input, nest_stack.clone(), redirects.len())
                     };
                     let redirect = attr.parse_args_with(parser)?;
                     redirects.push(redirect);
                 }
             }
 
-            let mut active_nests = nest_stack.clone();
-            active_nests.reverse();
+            let active_nests = nest_stack.clone();
             let mut active_layouts = layout_stack.clone();
             active_layouts.retain(|&id| !excluded.contains(&id));
 

+ 17 - 6
packages/router-macro/src/route.rs

@@ -186,26 +186,37 @@ impl Route {
     pub fn display_match(&self, nests: &[Nest]) -> TokenStream2 {
         let name = &self.route_name;
         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());
 
         match &self.ty {
             RouteType::Child(field) => {
+                let write_nests = self.nests.iter().map(|id| nests[id.0].write());
+                let write_segments = self.segments.iter().map(|s| s.write_segment());
                 let child = field.ident.as_ref().unwrap();
                 quote! {
                     Self::#name { #(#dynamic_segments,)* #child } => {
                         use std::fmt::Display;
-                        #(#write_layouts)*
-                        #(#write_segments)*
-                        #child.fmt(f);
+                        use std::fmt::Write;
+                        let mut route = String::new();
+                        {
+                            let f = &mut route;
+                            #(#write_nests)*
+                            #(#write_segments)*
+                        }
+                        if route.ends_with('/') {
+                            route.pop();
+                        }
+                        f.write_str(&route)?;
+                        #child.fmt(f)?;
                     }
                 }
             }
             RouteType::Leaf { .. } => {
+                let write_nests = self.nests.iter().map(|id| nests[id.0].write());
+                let write_segments = self.segments.iter().map(|s| s.write_segment());
                 quote! {
                     Self::#name { #(#dynamic_segments,)* } => {
-                        #(#write_layouts)*
+                        #(#write_nests)*
                         #(#write_segments)*
                         #write_query
                     }

+ 1 - 1
packages/router-macro/src/route_tree.rs

@@ -314,7 +314,7 @@ impl<'a> RouteTreeSegmentData<'a> {
                                 #(#children)*
                             }
                             else {
-                                errors.push(#error_enum_name::#enum_varient(#varient_parse_error::#error_ident))
+                                errors.push(#error_enum_name::#enum_varient(#varient_parse_error::#error_ident(segment.to_string())))
                             }
                         }
                     }

+ 5 - 4
packages/router-macro/src/segment.rs

@@ -58,10 +58,11 @@ impl RouteSegment {
                 quote! {
                     {
                         let mut segments = segments.clone();
-                        let parsed = if let Some(#segment) = segments.next() {
+                        let segment = segments.next();
+                        let parsed = if let Some(#segment) = segment {
                             Ok(())
                         } else {
-                            Err(#error_enum_name::#error_enum_varient(#inner_parse_enum::#error_name))
+                            Err(#error_enum_name::#error_enum_varient(#inner_parse_enum::#error_name(segment.map(|s|s.to_string()).unwrap_or_default())))
                         };
                         match parsed {
                             Ok(_) => {
@@ -234,8 +235,8 @@ pub(crate) fn create_error_type(
         let error_name = segment.error_name(i);
         match segment {
             RouteSegment::Static(index) => {
-                error_variants.push(quote! { #error_name });
-                display_match.push(quote! { Self::#error_name => write!(f, "Static segment '{}' did not match", #index)? });
+                error_variants.push(quote! { #error_name(String) });
+                display_match.push(quote! { Self::#error_name(found) => write!(f, "Static segment '{}' did not match instead found '{found}'", #index)? });
             }
             RouteSegment::Dynamic(ident, ty) => {
                 let missing_error = segment.missing_error_name().unwrap();

+ 38 - 29
packages/router/examples/simple_routes.rs

@@ -96,46 +96,55 @@ fn Route3(cx: Scope, dynamic: String) -> Element {
         p { "Site Map" }
         pre { "{site_map:#?}" }
         p { "Dynamic link" }
-        if let Ok(route) = parsed {
-            if route != current_route {
-                render! {
-                    Link {
-                        target: route.clone(),
-                        "{route}"
+        match parsed {
+            Ok(route) => {
+                if route != current_route {
+                    render! {
+                        Link {
+                            target: route.clone(),
+                            "{route}"
+                        }
                     }
                 }
+                else {
+                    None
+                }
             }
-            else {
-                None
+            Err(err) => {
+                render! {
+                    pre {
+                        color: "red",
+                        "Invalid route:\n{err}"
+                    }
+                }
             }
         }
-        else {
-            None
-        }
     }
 }
 
 #[rustfmt::skip]
 #[derive(Clone, Debug, PartialEq, Routable)]
 enum Route {
-    // Nests with parameters have types taken from child routes
-    #[nest("/user/:user_id")]
-        // Everything inside the nest has the added parameter `user_id: usize`
-        // UserFrame is a layout component that will receive the `user_id: usize` parameter
-        #[layout(UserFrame)]
-            #[route("/:dynamic?:query")]
-            Route1 {
-                // The type is taken from the first instance of the dynamic parameter
-                user_id: usize,
-                dynamic: usize,
-                query: String,
-                extra: String,
-            },
-            #[route("/hello_world")]
-            // You can opt out of the layout by using the `!` prefix
-            #[layout(!UserFrame)]
-            Route2 { user_id: usize },
-        #[end_layout]
+    #[nest("/test")]
+        // Nests with parameters have types taken from child routes
+        #[nest("/user/:user_id")]
+            // Everything inside the nest has the added parameter `user_id: usize`
+            // UserFrame is a layout component that will receive the `user_id: usize` parameter
+            #[layout(UserFrame)]
+                #[route("/:dynamic?:query")]
+                Route1 {
+                    // The type is taken from the first instance of the dynamic parameter
+                    user_id: usize,
+                    dynamic: usize,
+                    query: String,
+                    extra: String,
+                },
+                #[route("/hello_world")]
+                // You can opt out of the layout by using the `!` prefix
+                #[layout(!UserFrame)]
+                Route2 { user_id: usize },
+            #[end_layout]
+        #[end_nest]
     #[end_nest]
     #[redirect("/:id/user", |id: usize| Route::Route3 { dynamic: id.to_string()})]
     #[route("/:dynamic")]