allow nested routes
This commit is contained in:
1 parent
6569827b97
commit
2da1f7faa7
6 files changed
+346
-172
No files matched your search
@@ -5,9 +5,9 @@ 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 redirect::Redirect;
|
use redirect::Redirect;
|
||||||
use route::Route;
|
use route::{Route, RouteType};
|
||||||
use segment::RouteSegment;
|
use segment::RouteSegment;
|
||||||
use syn::{parse::ParseStream, parse_macro_input, Ident, Token};
|
use syn::{parse::ParseStream, parse_macro_input, Ident, Token, Type};
|
||||||
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
|
|
||||||
@@ -73,11 +73,13 @@ mod segment;
|
|||||||
/// #[redirect("/:id/user", |id: usize| Route::Route3 { dynamic: id.to_string()})]
|
/// #[redirect("/:id/user", |id: usize| Route::Route3 { dynamic: id.to_string()})]
|
||||||
/// #[route("/:dynamic")]
|
/// #[route("/:dynamic")]
|
||||||
/// Route3 { dynamic: String },
|
/// Route3 { dynamic: String },
|
||||||
|
/// #[child]
|
||||||
|
/// NestedRoute(NestedRoute),
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[proc_macro_derive(
|
#[proc_macro_derive(
|
||||||
Routable,
|
Routable,
|
||||||
attributes(route, nest, end_nest, layout, end_layout, redirect)
|
attributes(route, nest, end_nest, layout, end_layout, redirect, child)
|
||||||
)]
|
)]
|
||||||
pub fn routable(input: TokenStream) -> TokenStream {
|
pub fn routable(input: TokenStream) -> TokenStream {
|
||||||
let routes_enum = parse_macro_input!(input as syn::ItemEnum);
|
let routes_enum = parse_macro_input!(input as syn::ItemEnum);
|
||||||
@@ -124,12 +126,12 @@ pub fn routable(input: TokenStream) -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#error_type
|
#error_type
|
||||||
|
|
||||||
|
#display_impl
|
||||||
|
|
||||||
|
#routable_impl
|
||||||
|
|
||||||
#parse_impl
|
#parse_impl
|
||||||
|
|
||||||
#display_impl
|
|
||||||
|
|
||||||
#routable_impl
|
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
@@ -288,7 +290,33 @@ impl RouteEnum {
|
|||||||
let route = Route::parse(active_nests, active_layouts, variant.clone())?;
|
let route = Route::parse(active_nests, active_layouts, variant.clone())?;
|
||||||
|
|
||||||
// add the route to the site map
|
// add the route to the site map
|
||||||
if let Some(segment) = SiteMapSegment::new(&route.segments) {
|
let mut segment = SiteMapSegment::new(&route.segments);
|
||||||
|
if let RouteType::Child(child) = &route.ty {
|
||||||
|
let new_segment = SiteMapSegment {
|
||||||
|
segment_type: SegmentType::Child(child.clone()),
|
||||||
|
children: Vec::new(),
|
||||||
|
};
|
||||||
|
match &mut segment {
|
||||||
|
Some(segment) => {
|
||||||
|
fn set_last_child_to(
|
||||||
|
segment: &mut SiteMapSegment,
|
||||||
|
new_segment: SiteMapSegment,
|
||||||
|
) {
|
||||||
|
if let Some(last) = segment.children.last_mut() {
|
||||||
|
set_last_child_to(last, new_segment);
|
||||||
|
} else {
|
||||||
|
segment.children = vec![new_segment];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set_last_child_to(segment, new_segment);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
segment = Some(new_segment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(segment) = segment {
|
||||||
let parent = site_map_stack.last_mut();
|
let parent = site_map_stack.last_mut();
|
||||||
let children = match parent {
|
let children = match parent {
|
||||||
Some(parent) => &mut parent.last_mut().unwrap().children,
|
Some(parent) => &mut parent.last_mut().unwrap().children,
|
||||||
@@ -443,31 +471,13 @@ impl RouteEnum {
|
|||||||
let name = &self.name;
|
let name = &self.name;
|
||||||
let site_map = &self.site_map;
|
let site_map = &self.site_map;
|
||||||
|
|
||||||
let mut layers = Vec::new();
|
let mut matches = Vec::new();
|
||||||
|
|
||||||
loop {
|
// Collect all routes matches
|
||||||
let index = layers.len();
|
for route in &self.routes {
|
||||||
let mut routable_match = Vec::new();
|
matches.push(route.routable_match(&self.layouts, &self.nests));
|
||||||
|
|
||||||
// Collect all routes that match the current layer
|
|
||||||
for route in &self.routes {
|
|
||||||
if let Some(matched) = route.routable_match(&self.layouts, &self.nests, index) {
|
|
||||||
routable_match.push(matched);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All routes are exhausted
|
|
||||||
if routable_match.is_empty() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
layers.push(quote! {
|
|
||||||
#(#routable_match)*
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let index_iter = 0..layers.len();
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
impl dioxus_router::routable::Routable for #name where Self: Clone {
|
impl dioxus_router::routable::Routable for #name where Self: Clone {
|
||||||
const SITE_MAP: &'static [dioxus_router::routable::SiteMapSegment] = &[
|
const SITE_MAP: &'static [dioxus_router::routable::SiteMapSegment] = &[
|
||||||
@@ -476,15 +486,8 @@ impl RouteEnum {
|
|||||||
|
|
||||||
fn render<'a>(&self, cx: &'a ScopeState, level: usize) -> Element<'a> {
|
fn render<'a>(&self, cx: &'a ScopeState, level: usize) -> Element<'a> {
|
||||||
let myself = self.clone();
|
let myself = self.clone();
|
||||||
match level {
|
match (level, myself) {
|
||||||
#(
|
#(#matches)*
|
||||||
#index_iter => {
|
|
||||||
match myself {
|
|
||||||
#layers
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)*
|
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -521,14 +524,25 @@ impl SiteMapSegment {
|
|||||||
impl ToTokens for SiteMapSegment {
|
impl ToTokens for SiteMapSegment {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||||
let segment_type = &self.segment_type;
|
let segment_type = &self.segment_type;
|
||||||
let children = &self.children;
|
let children = if let SegmentType::Child(ty) = &self.segment_type {
|
||||||
|
quote! { #ty::SITE_MAP }
|
||||||
|
} else {
|
||||||
|
let children = self
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|child| child.to_token_stream())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
quote! {
|
||||||
|
&[
|
||||||
|
#(#children,)*
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
tokens.extend(quote! {
|
tokens.extend(quote! {
|
||||||
dioxus_router::routable::SiteMapSegment {
|
dioxus_router::routable::SiteMapSegment {
|
||||||
segment_type: #segment_type,
|
segment_type: #segment_type,
|
||||||
children: &[
|
children: #children,
|
||||||
#(#children,)*
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -538,6 +552,7 @@ enum SegmentType {
|
|||||||
Static(String),
|
Static(String),
|
||||||
Dynamic(String),
|
Dynamic(String),
|
||||||
CatchAll(String),
|
CatchAll(String),
|
||||||
|
Child(Type),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for SegmentType {
|
impl ToTokens for SegmentType {
|
||||||
@@ -552,6 +567,9 @@ impl ToTokens for SegmentType {
|
|||||||
SegmentType::CatchAll(s) => {
|
SegmentType::CatchAll(s) => {
|
||||||
tokens.extend(quote! { dioxus_router::routable::SegmentType::CatchAll(#s) })
|
tokens.extend(quote! { dioxus_router::routable::SegmentType::CatchAll(#s) })
|
||||||
}
|
}
|
||||||
|
SegmentType::Child(_) => {
|
||||||
|
tokens.extend(quote! { dioxus_router::routable::SegmentType::Child })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+195
-110
@@ -3,6 +3,7 @@ use syn::parse::Parse;
|
|||||||
use syn::parse::ParseStream;
|
use syn::parse::ParseStream;
|
||||||
use syn::parse_quote;
|
use syn::parse_quote;
|
||||||
use syn::Path;
|
use syn::Path;
|
||||||
|
use syn::Type;
|
||||||
use syn::{Ident, LitStr};
|
use syn::{Ident, LitStr};
|
||||||
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
@@ -40,18 +41,28 @@ impl Parse for RouteArgs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ChildArgs {
|
||||||
|
route: LitStr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for ChildArgs {
|
||||||
|
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||||
|
let route = input.parse::<LitStr>()?;
|
||||||
|
|
||||||
|
Ok(ChildArgs { route })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Route {
|
pub(crate) struct Route {
|
||||||
pub route_name: Ident,
|
pub route_name: Ident,
|
||||||
pub comp_name: Path,
|
pub ty: RouteType,
|
||||||
pub props_name: Path,
|
|
||||||
pub route: String,
|
pub route: String,
|
||||||
pub segments: Vec<RouteSegment>,
|
pub segments: Vec<RouteSegment>,
|
||||||
pub query: Option<QuerySegment>,
|
pub query: Option<QuerySegment>,
|
||||||
pub nests: Vec<NestId>,
|
pub nests: Vec<NestId>,
|
||||||
pub layouts: Vec<LayoutId>,
|
pub layouts: Vec<LayoutId>,
|
||||||
pub variant: syn::Variant,
|
fields: Vec<(Ident, Type)>,
|
||||||
fields: syn::FieldsNamed,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Route {
|
impl Route {
|
||||||
@@ -63,62 +74,93 @@ impl Route {
|
|||||||
let route_attr = variant
|
let route_attr = variant
|
||||||
.attrs
|
.attrs
|
||||||
.iter()
|
.iter()
|
||||||
.find(|attr| attr.path.is_ident("route"))
|
.find(|attr| attr.path.is_ident("route"));
|
||||||
.ok_or_else(|| {
|
let route;
|
||||||
syn::Error::new_spanned(
|
let ty;
|
||||||
variant.clone(),
|
|
||||||
"Routable variants must have a #[route(...)] attribute",
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let route_name = variant.ident.clone();
|
let route_name = variant.ident.clone();
|
||||||
let args = route_attr.parse_args::<RouteArgs>()?;
|
match route_attr {
|
||||||
let route = args.route.value();
|
Some(attr) => {
|
||||||
let comp_name = args.comp_name.unwrap_or_else(|| parse_quote!(#route_name));
|
let args = attr.parse_args::<RouteArgs>()?;
|
||||||
let props_name = args.props_name.unwrap_or_else(|| {
|
let comp_name = args.comp_name.unwrap_or_else(|| parse_quote!(#route_name));
|
||||||
let last = format_ident!(
|
let props_name = args.props_name.unwrap_or_else(|| {
|
||||||
"{}Props",
|
let last = format_ident!(
|
||||||
comp_name.segments.last().unwrap().ident.to_string()
|
"{}Props",
|
||||||
);
|
comp_name.segments.last().unwrap().ident.to_string()
|
||||||
let mut segments = comp_name.segments.clone();
|
);
|
||||||
segments.pop();
|
let mut segments = comp_name.segments.clone();
|
||||||
segments.push(last.into());
|
segments.pop();
|
||||||
Path {
|
segments.push(last.into());
|
||||||
leading_colon: None,
|
Path {
|
||||||
segments,
|
leading_colon: None,
|
||||||
|
segments,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ty = RouteType::Leaf {
|
||||||
|
component: comp_name,
|
||||||
|
props: props_name,
|
||||||
|
};
|
||||||
|
route = args.route.value();
|
||||||
}
|
}
|
||||||
});
|
None => {
|
||||||
|
if let Some(route_attr) = variant
|
||||||
let named_fields = match &variant.fields {
|
.attrs
|
||||||
syn::Fields::Named(fields) => fields,
|
.iter()
|
||||||
_ => {
|
.find(|attr| attr.path.is_ident("child"))
|
||||||
return Err(syn::Error::new_spanned(
|
{
|
||||||
variant.clone(),
|
let args = route_attr.parse_args::<ChildArgs>()?;
|
||||||
"Routable variants must have named fields",
|
route = args.route.value();
|
||||||
))
|
match &variant.fields {
|
||||||
|
syn::Fields::Unnamed(fields) => {
|
||||||
|
if fields.unnamed.len() != 1 {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
variant.clone(),
|
||||||
|
"Routable variants with a #[child(...)] attribute must have exactly one field",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
ty = RouteType::Child(fields.unnamed[0].ty.clone());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
variant.clone(),
|
||||||
|
"Routable variants with a #[child(...)] attribute must have exactly one field",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
variant.clone(),
|
||||||
|
"Routable variants must either have a #[route(...)] attribute or a #[child(...)] attribute",
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (route_segments, query) = parse_route_segments(
|
let fields = match &variant.fields {
|
||||||
variant.ident.span(),
|
syn::Fields::Named(fields) => fields
|
||||||
named_fields
|
|
||||||
.named
|
.named
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| (f.ident.as_ref().unwrap(), &f.ty)),
|
.map(|f| (f.ident.clone().unwrap(), f.ty.clone()))
|
||||||
&route,
|
.collect(),
|
||||||
)?;
|
_ => Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (route_segments, query) = {
|
||||||
|
parse_route_segments(
|
||||||
|
variant.ident.span(),
|
||||||
|
fields.iter().map(|f| (&f.0, &f.1)),
|
||||||
|
&route,
|
||||||
|
)?
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
comp_name,
|
ty,
|
||||||
props_name,
|
|
||||||
route_name,
|
route_name,
|
||||||
segments: route_segments,
|
segments: route_segments,
|
||||||
route,
|
route,
|
||||||
query,
|
query,
|
||||||
nests,
|
nests,
|
||||||
layouts,
|
layouts,
|
||||||
fields: named_fields.clone(),
|
fields,
|
||||||
variant,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,101 +171,138 @@ impl Route {
|
|||||||
let write_segments = self.segments.iter().map(|s| s.write_segment());
|
let write_segments = self.segments.iter().map(|s| s.write_segment());
|
||||||
let write_query = self.query.as_ref().map(|q| q.write());
|
let write_query = self.query.as_ref().map(|q| q.write());
|
||||||
|
|
||||||
quote! {
|
match &self.ty {
|
||||||
Self::#name { #(#dynamic_segments,)* } => {
|
RouteType::Child(_) => {
|
||||||
#(#write_layouts)*
|
quote! {
|
||||||
#(#write_segments)*
|
Self::#name(child) => {
|
||||||
#write_query
|
use std::fmt::Display;
|
||||||
|
#(#write_layouts)*
|
||||||
|
#(#write_segments)*
|
||||||
|
child.fmt(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RouteType::Leaf { .. } => {
|
||||||
|
quote! {
|
||||||
|
Self::#name { #(#dynamic_segments,)* } => {
|
||||||
|
#(#write_layouts)*
|
||||||
|
#(#write_segments)*
|
||||||
|
#write_query
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn routable_match(
|
pub fn routable_match(&self, layouts: &[Layout], nests: &[Nest]) -> TokenStream2 {
|
||||||
&self,
|
|
||||||
layouts: &[Layout],
|
|
||||||
nests: &[Nest],
|
|
||||||
index: usize,
|
|
||||||
) -> Option<TokenStream2> {
|
|
||||||
let name = &self.route_name;
|
let name = &self.route_name;
|
||||||
let name_str = name.to_string();
|
let name_str = name.to_string();
|
||||||
let dynamic_segments = self.dynamic_segments();
|
|
||||||
|
|
||||||
match index.cmp(&self.layouts.len()) {
|
let mut tokens = TokenStream2::new();
|
||||||
std::cmp::Ordering::Less => {
|
|
||||||
let layout = self.layouts[index];
|
// First match all layouts
|
||||||
let render_layout = layouts[layout.0].routable_match(nests);
|
for (idx, layout_id) in self.layouts.iter().copied().enumerate() {
|
||||||
// This is a layout
|
let render_layout = layouts[layout_id.0].routable_match(nests);
|
||||||
Some(quote! {
|
let dynamic_segments = self.dynamic_segments();
|
||||||
|
// This is a layout
|
||||||
|
tokens.extend(quote! {
|
||||||
|
#[allow(unused)]
|
||||||
|
(#idx, Self::#name { #(#dynamic_segments,)* }) => {
|
||||||
|
#render_layout
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then match the route
|
||||||
|
let last_index = self.layouts.len();
|
||||||
|
tokens.extend(match &self.ty {
|
||||||
|
RouteType::Child(_) => {
|
||||||
|
quote! {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
Self::#name { #(#dynamic_segments,)* } => {
|
(#last_index.., Self::#name(child_route)) => {
|
||||||
#render_layout
|
child_route.render(cx, level - #last_index)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
std::cmp::Ordering::Equal => {
|
RouteType::Leaf { component, props } => {
|
||||||
|
let dynamic_segments = self.dynamic_segments();
|
||||||
let dynamic_segments_from_route = self.dynamic_segments();
|
let dynamic_segments_from_route = self.dynamic_segments();
|
||||||
let props_name = &self.props_name;
|
quote! {
|
||||||
let comp_name = &self.comp_name;
|
|
||||||
// This is the final route
|
|
||||||
Some(quote! {
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
Self::#name { #(#dynamic_segments,)* } => {
|
(#last_index, Self::#name { #(#dynamic_segments,)* }) => {
|
||||||
let comp = #props_name { #(#dynamic_segments_from_route,)* };
|
let comp = #props { #(#dynamic_segments_from_route,)* };
|
||||||
let dynamic = cx.component(#comp_name, comp, #name_str);
|
let dynamic = cx.component(#component, comp, #name_str);
|
||||||
render! {
|
render! {
|
||||||
dynamic
|
dynamic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
_ => None,
|
});
|
||||||
}
|
|
||||||
|
tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dynamic_segments(&self) -> impl Iterator<Item = TokenStream2> + '_ {
|
fn dynamic_segments(&self) -> impl Iterator<Item = TokenStream2> + '_ {
|
||||||
self.fields.named.iter().map(|f| {
|
self.fields.iter().map(|(name, _)| {
|
||||||
let name = f.ident.as_ref().unwrap();
|
|
||||||
quote! {#name}
|
quote! {#name}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn construct(&self, nests: &[Nest], enum_name: Ident) -> TokenStream2 {
|
pub fn construct(&self, nests: &[Nest], enum_name: Ident) -> TokenStream2 {
|
||||||
let segments = self.fields.named.iter().map(|f| {
|
match &self.ty {
|
||||||
let name = f.ident.as_ref().unwrap();
|
RouteType::Child(ty) => {
|
||||||
|
let name = &self.route_name;
|
||||||
|
|
||||||
let mut from_route = false;
|
quote! {
|
||||||
|
{
|
||||||
for id in &self.nests {
|
let mut trailing = String::new();
|
||||||
let nest = &nests[id.0];
|
for seg in segments {
|
||||||
if nest.dynamic_segments_names().any(|i| &i == name) {
|
trailing += seg;
|
||||||
from_route = true
|
trailing += "/";
|
||||||
}
|
}
|
||||||
}
|
trailing.pop();
|
||||||
for segment in &self.segments {
|
#enum_name::#name(#ty::from_str(&trailing).unwrap())
|
||||||
if let RouteSegment::Dynamic(other, ..) = segment {
|
|
||||||
if other == name {
|
|
||||||
from_route = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(query) = &self.query {
|
RouteType::Leaf { .. } => {
|
||||||
if &query.ident == name {
|
let segments = self.fields.iter().map(|(name, _)| {
|
||||||
from_route = true
|
let mut from_route = false;
|
||||||
|
|
||||||
|
for id in &self.nests {
|
||||||
|
let nest = &nests[id.0];
|
||||||
|
if nest.dynamic_segments_names().any(|i| &i == name) {
|
||||||
|
from_route = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for segment in &self.segments {
|
||||||
|
if let RouteSegment::Dynamic(other, ..) = segment {
|
||||||
|
if other == name {
|
||||||
|
from_route = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(query) = &self.query {
|
||||||
|
if &query.ident == name {
|
||||||
|
from_route = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if from_route {
|
||||||
|
quote! {#name}
|
||||||
|
} else {
|
||||||
|
quote! {#name: Default::default()}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let name = &self.route_name;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#enum_name::#name {
|
||||||
|
#(#segments,)*
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if from_route {
|
|
||||||
quote! {#name}
|
|
||||||
} else {
|
|
||||||
quote! {#name: Default::default()}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let name = &self.route_name;
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#enum_name::#name {
|
|
||||||
#(#segments,)*
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,3 +323,9 @@ impl Route {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum RouteType {
|
||||||
|
Child(Type),
|
||||||
|
Leaf { component: Path, props: Path },
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ use syn::Ident;
|
|||||||
use crate::{
|
use crate::{
|
||||||
nest::{Nest, NestId},
|
nest::{Nest, NestId},
|
||||||
redirect::Redirect,
|
redirect::Redirect,
|
||||||
route::Route,
|
route::{Route, RouteType},
|
||||||
segment::{static_segment_idx, RouteSegment},
|
segment::{static_segment_idx, RouteSegment},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -334,11 +334,14 @@ impl<'a> RouteTreeSegmentData<'a> {
|
|||||||
let construct_variant = route.construct(nests, enum_name);
|
let construct_variant = route.construct(nests, enum_name);
|
||||||
let parse_query = route.parse_query();
|
let parse_query = route.parse_query();
|
||||||
|
|
||||||
let insure_not_trailing = route
|
let insure_not_trailing = match route.ty {
|
||||||
.segments
|
RouteType::Leaf { .. } => route
|
||||||
.last()
|
.segments
|
||||||
.map(|seg| !matches!(seg, RouteSegment::CatchAll(_, _)))
|
.last()
|
||||||
.unwrap_or(true);
|
.map(|seg| !matches!(seg, RouteSegment::CatchAll(_, _)))
|
||||||
|
.unwrap_or(true),
|
||||||
|
RouteType::Child(_) => false,
|
||||||
|
};
|
||||||
|
|
||||||
print_route_segment(
|
print_route_segment(
|
||||||
route_segments.peekable(),
|
route_segments.peekable(),
|
||||||
|
|||||||
@@ -28,22 +28,25 @@ async fn main() {
|
|||||||
.invalidate_after(Duration::from_secs(10))
|
.invalidate_after(Duration::from_secs(10))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"SITE MAP:\n{}",
|
||||||
|
Route::SITE_MAP
|
||||||
|
.iter()
|
||||||
|
.flat_map(|route| route.flatten().into_iter())
|
||||||
|
.map(|route| {
|
||||||
|
route
|
||||||
|
.iter()
|
||||||
|
.map(|segment| segment.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("")
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
);
|
||||||
|
|
||||||
pre_cache_static_routes::<Route, _>(&mut renderer)
|
pre_cache_static_routes::<Route, _>(&mut renderer)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
for _ in 0..1_000_000 {
|
|
||||||
for id in 0..10 {
|
|
||||||
render_route(
|
|
||||||
&mut renderer,
|
|
||||||
Route::Post { id },
|
|
||||||
&mut tokio::io::sink(),
|
|
||||||
|_| {},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline_props]
|
#[inline_props]
|
||||||
@@ -82,6 +85,64 @@ fn Home(cx: Scope) -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod inner {
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_router::prelude::*;
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Routable)]
|
||||||
|
pub enum Route {
|
||||||
|
#[nest("/blog")]
|
||||||
|
#[route("/")]
|
||||||
|
Blog {},
|
||||||
|
#[route("/post/index")]
|
||||||
|
PostHome {},
|
||||||
|
#[route("/post/:id")]
|
||||||
|
Post {
|
||||||
|
id: usize,
|
||||||
|
},
|
||||||
|
#[end_nest]
|
||||||
|
#[route("/")]
|
||||||
|
Home {},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline_props]
|
||||||
|
fn Blog(cx: Scope) -> Element {
|
||||||
|
render! {
|
||||||
|
div {
|
||||||
|
"Blog"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline_props]
|
||||||
|
fn Post(cx: Scope, id: usize) -> Element {
|
||||||
|
render! {
|
||||||
|
div {
|
||||||
|
"PostId: {id}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline_props]
|
||||||
|
fn PostHome(cx: Scope) -> Element {
|
||||||
|
render! {
|
||||||
|
div {
|
||||||
|
"Post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline_props]
|
||||||
|
fn Home(cx: Scope) -> Element {
|
||||||
|
render! {
|
||||||
|
div {
|
||||||
|
"Home"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[derive(Clone, Debug, PartialEq, Routable)]
|
#[derive(Clone, Debug, PartialEq, Routable)]
|
||||||
enum Route {
|
enum Route {
|
||||||
@@ -97,4 +158,6 @@ enum Route {
|
|||||||
#[end_nest]
|
#[end_nest]
|
||||||
#[route("/")]
|
#[route("/")]
|
||||||
Home {},
|
Home {},
|
||||||
|
#[child("/hello_world")]
|
||||||
|
HelloWorldRoute(inner::Route)
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,7 @@ where
|
|||||||
let mut full_path = String::new();
|
let mut full_path = String::new();
|
||||||
for segment in &route {
|
for segment in &route {
|
||||||
match segment {
|
match segment {
|
||||||
|
SegmentType::Child => {}
|
||||||
SegmentType::Static(s) => {
|
SegmentType::Static(s) => {
|
||||||
full_path += "/";
|
full_path += "/";
|
||||||
full_path += s;
|
full_path += s;
|
||||||
@@ -43,6 +44,7 @@ where
|
|||||||
render_route(renderer, route, &mut tokio::io::sink(), |_| {}).await?;
|
render_route(renderer, route, &mut tokio::io::sink(), |_| {}).await?;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
log::info!("@ route: {}", full_path);
|
||||||
log::error!("Error pre-caching static route: {}", e);
|
log::error!("Error pre-caching static route: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,12 +172,15 @@ pub enum SegmentType {
|
|||||||
Dynamic(&'static str),
|
Dynamic(&'static str),
|
||||||
/// A catch all route segment
|
/// A catch all route segment
|
||||||
CatchAll(&'static str),
|
CatchAll(&'static str),
|
||||||
|
/// A child router
|
||||||
|
Child,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for SegmentType {
|
impl Display for SegmentType {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match &self {
|
match &self {
|
||||||
SegmentType::Static(s) => write!(f, "/{}", s),
|
SegmentType::Static(s) => write!(f, "/{}", s),
|
||||||
|
SegmentType::Child => Ok(()),
|
||||||
SegmentType::Dynamic(s) => write!(f, "/:{}", s),
|
SegmentType::Dynamic(s) => write!(f, "/:{}", s),
|
||||||
SegmentType::CatchAll(s) => write!(f, "/:...{}", s),
|
SegmentType::CatchAll(s) => write!(f, "/:...{}", s),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user