mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-27 14:40:44 +00:00
implement spread segments
This commit is contained in:
parent
2aadeb8046
commit
ee763d52e1
5 changed files with 238 additions and 123 deletions
|
@ -67,20 +67,36 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ToRouteSegments {
|
pub trait ToRouteSegments {
|
||||||
fn to_route_segments(&self) -> Vec<String>;
|
fn display_route_segements(self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, T: std::fmt::Display> ToRouteSegments for I
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = T>,
|
||||||
|
{
|
||||||
|
fn display_route_segements(self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for segment in self {
|
||||||
|
write!(f, "/")?;
|
||||||
|
write!(f, "{}", segment)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FromRouteSegments: Sized {
|
pub trait FromRouteSegments: Sized {
|
||||||
type Err;
|
type Err;
|
||||||
|
|
||||||
fn from_route_segments(segments: &[&str], query: &str) -> Result<Self, Self::Err>;
|
fn from_route_segments(segments: &[&str]) -> Result<Self, Self::Err>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: FromRouteSegment> FromRouteSegments for Vec<T> {
|
impl<I: std::iter::FromIterator<String>> FromRouteSegments for I {
|
||||||
type Err = <T as FromRouteSegment>::Err;
|
type Err = <String as FromRouteSegment>::Err;
|
||||||
|
|
||||||
fn from_route_segments(segments: &[&str], query: &str) -> Result<Self, Self::Err> {
|
fn from_route_segments(segments: &[&str]) -> Result<Self, Self::Err> {
|
||||||
segments.iter().map(|s| T::from_route_segment(s)).collect()
|
segments
|
||||||
|
.iter()
|
||||||
|
.map(|s| String::from_route_segment(s))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_router_macro::*;
|
|
||||||
use dioxus_router_core::*;
|
use dioxus_router_core::*;
|
||||||
|
use dioxus_router_macro::*;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[inline_props]
|
#[inline_props]
|
||||||
|
@ -43,7 +43,16 @@ fn Route4(cx: Scope, number1: u32, number2: u32) -> Element {
|
||||||
fn Route5(cx: Scope, query: String) -> Element {
|
fn Route5(cx: Scope, query: String) -> Element {
|
||||||
render! {
|
render! {
|
||||||
div{
|
div{
|
||||||
"Route5"
|
"Route5: {query}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline_props]
|
||||||
|
fn Route6(cx: Scope, extra: Vec<String>) -> Element {
|
||||||
|
render! {
|
||||||
|
div{
|
||||||
|
"Route5: {extra:?}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,9 +69,9 @@ enum Route {
|
||||||
#[route("/(number1)/(number2)" Route4)]
|
#[route("/(number1)/(number2)" Route4)]
|
||||||
Route4 { number1: u32, number2: u32 },
|
Route4 { number1: u32, number2: u32 },
|
||||||
#[route("/?(query)" Route5)]
|
#[route("/?(query)" Route5)]
|
||||||
Route5 {
|
Route5 { query: String },
|
||||||
query: String,
|
#[route("/(...extra)" Route6)]
|
||||||
},
|
Route6 { extra: Vec<String> },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -82,6 +91,25 @@ fn display_works() {
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(route.to_string(), "/hello_world2");
|
assert_eq!(route.to_string(), "/hello_world2");
|
||||||
|
|
||||||
|
let route = Route::Route4 {
|
||||||
|
number1: 1234,
|
||||||
|
number2: 5678,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(route.to_string(), "/1234/5678");
|
||||||
|
|
||||||
|
let route = Route::Route5 {
|
||||||
|
query: "hello".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(route.to_string(), "/?hello");
|
||||||
|
|
||||||
|
let route = Route::Route6 {
|
||||||
|
extra: vec!["hello".to_string(), "world".to_string()],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(route.to_string(), "/hello/world");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -114,12 +142,6 @@ fn from_string_works() {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let w = "/hello_world/-1";
|
|
||||||
match Route::from_str(w) {
|
|
||||||
Ok(r) => panic!("should not parse {r:?}"),
|
|
||||||
Err(err) => println!("{err}"),
|
|
||||||
}
|
|
||||||
|
|
||||||
let w = "/?x=1234&y=hello";
|
let w = "/?x=1234&y=hello";
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Route::from_str(w),
|
Route::from_str(w),
|
||||||
|
@ -127,6 +149,18 @@ fn from_string_works() {
|
||||||
query: "x=1234&y=hello".to_string()
|
query: "x=1234&y=hello".to_string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let w = "/hello_world/hello_world/hello_world";
|
||||||
|
assert_eq!(
|
||||||
|
Route::from_str(w),
|
||||||
|
Ok(Route::Route6 {
|
||||||
|
extra: vec![
|
||||||
|
"hello_world".to_string(),
|
||||||
|
"hello_world".to_string(),
|
||||||
|
"hello_world".to_string()
|
||||||
|
]
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -161,4 +195,16 @@ fn round_trip() {
|
||||||
query: string.to_string(),
|
query: string.to_string(),
|
||||||
};
|
};
|
||||||
assert_eq!(Route::from_str(&route.to_string()), Ok(route));
|
assert_eq!(Route::from_str(&route.to_string()), Ok(route));
|
||||||
|
|
||||||
|
// Route5
|
||||||
|
let route = Route::Route6 {
|
||||||
|
extra: vec![
|
||||||
|
"hello_world".to_string(),
|
||||||
|
"hello_world".to_string(),
|
||||||
|
"hello_world".to_string(),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
assert_eq!(Route::from_str(&route.to_string()), Ok(route));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub fn derive_routable(input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
let route_enum = match RouteEnum::parse(routes_enum) {
|
let route_enum = match RouteEnum::parse(routes_enum) {
|
||||||
Ok(route_enum) => route_enum,
|
Ok(route_enum) => route_enum,
|
||||||
Err(err) => return TokenStream2::from(err.to_compile_error()).into(),
|
Err(err) => return err.to_compile_error().into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let error_type = route_enum.error_type();
|
let error_type = route_enum.error_type();
|
||||||
|
@ -118,9 +118,7 @@ impl RouteEnum {
|
||||||
let mut segments = route.split('/');
|
let mut segments = route.split('/');
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
if let Some(segment) = segments.next() {
|
#(#tokens)*
|
||||||
#(#tokens)*
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(RouteParseError {
|
Err(RouteParseError {
|
||||||
attempted_routes: errors,
|
attempted_routes: errors,
|
||||||
|
|
|
@ -93,8 +93,7 @@ impl Route {
|
||||||
|
|
||||||
pub fn routable_match(&self) -> TokenStream2 {
|
pub fn routable_match(&self) -> TokenStream2 {
|
||||||
let name = &self.route_name;
|
let name = &self.route_name;
|
||||||
let dynamic_segments: Vec<_> = self
|
let dynamic_segments: Vec<_> = self.dynamic_segments().collect();
|
||||||
.dynamic_segments().collect();
|
|
||||||
let props_name = &self.props_name;
|
let props_name = &self.props_name;
|
||||||
let comp_name = &self.comp_name;
|
let comp_name = &self.comp_name;
|
||||||
|
|
||||||
|
@ -110,7 +109,7 @@ impl Route {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dynamic_segments(&self)-> impl Iterator<Item = TokenStream2> +'_{
|
fn dynamic_segments(&self) -> impl Iterator<Item = TokenStream2> + '_ {
|
||||||
let segments = self.route_segments.iter().filter_map(|seg| {
|
let segments = self.route_segments.iter().filter_map(|seg| {
|
||||||
seg.name().map(|name| {
|
seg.name().map(|name| {
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -118,18 +117,22 @@ impl Route {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
let query = self.query.as_ref().map(|q| {
|
let query = self
|
||||||
let name = q.name();
|
.query
|
||||||
quote! {
|
.as_ref()
|
||||||
#name
|
.map(|q| {
|
||||||
}
|
let name = q.name();
|
||||||
}).into_iter();
|
quote! {
|
||||||
|
#name
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into_iter();
|
||||||
|
|
||||||
segments.chain(query)
|
segments.chain(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn construct(&self, enum_name: Ident) -> TokenStream2 {
|
pub fn construct(&self, enum_name: Ident) -> TokenStream2 {
|
||||||
let segments = self.dynamic_segments();
|
let segments = self.dynamic_segments();
|
||||||
let name = &self.route_name;
|
let name = &self.route_name;
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -157,11 +160,14 @@ impl Route {
|
||||||
display_match.push(quote! { Self::#error_name => write!(f, "Static segment '{}' did not match", #index)? });
|
display_match.push(quote! { Self::#error_name => write!(f, "Static segment '{}' did not match", #index)? });
|
||||||
}
|
}
|
||||||
RouteSegment::Dynamic(ident, ty) => {
|
RouteSegment::Dynamic(ident, ty) => {
|
||||||
error_variants.push(quote! { #error_name(<#ty as std::str::FromStr>::Err) });
|
let missing_error = segment.missing_error_name().unwrap();
|
||||||
|
error_variants.push(quote! { #error_name(<#ty as dioxus_router_core::router::FromRouteSegment>::Err) });
|
||||||
display_match.push(quote! { Self::#error_name(err) => write!(f, "Dynamic segment '({}:{})' did not match: {}", stringify!(#ident), stringify!(#ty), err)? });
|
display_match.push(quote! { Self::#error_name(err) => write!(f, "Dynamic segment '({}:{})' did not match: {}", stringify!(#ident), stringify!(#ty), err)? });
|
||||||
|
error_variants.push(quote! { #missing_error });
|
||||||
|
display_match.push(quote! { Self::#missing_error => write!(f, "Dynamic segment '({}:{})' was missing", stringify!(#ident), stringify!(#ty))? });
|
||||||
}
|
}
|
||||||
RouteSegment::CatchAll(ident, ty) => {
|
RouteSegment::CatchAll(ident, ty) => {
|
||||||
error_variants.push(quote! { #error_name(<#ty as std::str::FromStr>::Err) });
|
error_variants.push(quote! { #error_name(<#ty as dioxus_router_core::router::FromRouteSegments>::Err) });
|
||||||
display_match.push(quote! { Self::#error_name(err) => write!(f, "Catch-all segment '({}:{})' did not match: {}", stringify!(#ident), stringify!(#ty), err)? });
|
display_match.push(quote! { Self::#error_name(err) => write!(f, "Catch-all segment '({}:{})' did not match: {}", stringify!(#ident), stringify!(#ty), err)? });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,6 +201,13 @@ impl Route {
|
||||||
None => quote! {},
|
None => quote! {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ends_with_catch_all(&self) -> bool {
|
||||||
|
self.route_segments
|
||||||
|
.last()
|
||||||
|
.map(|seg| matches!(seg, RouteSegment::CatchAll(..)))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for Route {
|
impl ToTokens for Route {
|
||||||
|
@ -211,7 +224,7 @@ impl ToTokens for Route {
|
||||||
let route = dir.join("src").join("pages").join(with_extension.clone());
|
let route = dir.join("src").join("pages").join(with_extension.clone());
|
||||||
|
|
||||||
// check if the route exists or if not use the index route
|
// check if the route exists or if not use the index route
|
||||||
let route = if route.exists() && without_leading_slash != "" {
|
let route = if route.exists() && !without_leading_slash.is_empty() {
|
||||||
with_extension.to_str().unwrap().to_string()
|
with_extension.to_str().unwrap().to_string()
|
||||||
} else {
|
} else {
|
||||||
route_path.join("index.rs").to_str().unwrap().to_string()
|
route_path.join("index.rs").to_str().unwrap().to_string()
|
||||||
|
@ -259,13 +272,13 @@ fn parse_route_segments(
|
||||||
let spread = segment.starts_with("(...");
|
let spread = segment.starts_with("(...");
|
||||||
|
|
||||||
let ident = if spread {
|
let ident = if spread {
|
||||||
segment[3..segment.len() - 1].to_string()
|
segment[4..segment.len() - 1].to_string()
|
||||||
} else {
|
} else {
|
||||||
segment[1..segment.len() - 1].to_string()
|
segment[1..segment.len() - 1].to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
let field = varient.fields.iter().find(|field| match field.ident {
|
let field = varient.fields.iter().find(|field| match field.ident {
|
||||||
Some(ref field_ident) => field_ident.to_string() == ident,
|
Some(ref field_ident) => *field_ident == ident,
|
||||||
None => false,
|
None => false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -361,7 +374,7 @@ impl RouteSegment {
|
||||||
match self {
|
match self {
|
||||||
Self::Static(segment) => quote! { write!(f, "/{}", #segment)?; },
|
Self::Static(segment) => quote! { write!(f, "/{}", #segment)?; },
|
||||||
Self::Dynamic(ident, _) => quote! { write!(f, "/{}", #ident)?; },
|
Self::Dynamic(ident, _) => quote! { write!(f, "/{}", #ident)?; },
|
||||||
Self::CatchAll(ident, _) => quote! { write!(f, "/{}", #ident)?; },
|
Self::CatchAll(ident, _) => quote! { #ident.display_route_segements(f)?; },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,32 +386,81 @@ impl RouteSegment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn missing_error_name(&self) -> Option<Ident> {
|
||||||
|
match self {
|
||||||
|
Self::Dynamic(ident, _) => Some(format_ident!("{}MissingError", ident)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn try_parse(
|
pub fn try_parse(
|
||||||
&self,
|
&self,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
error_enum_name: &Ident,
|
error_enum_name: &Ident,
|
||||||
error_enum_varient: &Ident,
|
error_enum_varient: &Ident,
|
||||||
inner_parse_enum: &Ident,
|
inner_parse_enum: &Ident,
|
||||||
|
parse_children: TokenStream2,
|
||||||
) -> TokenStream2 {
|
) -> TokenStream2 {
|
||||||
let error_name = self.error_name(idx);
|
let error_name = self.error_name(idx);
|
||||||
match self {
|
match self {
|
||||||
Self::Static(segment) => {
|
Self::Static(segment) => {
|
||||||
quote! {
|
quote! {
|
||||||
let parsed = if segment == #segment {
|
{
|
||||||
Ok(())
|
let mut segments = segments.clone();
|
||||||
} else {
|
let parsed = if let Some(#segment) = segments.next() {
|
||||||
Err(#error_enum_name::#error_enum_varient(#inner_parse_enum::#error_name))
|
Ok(())
|
||||||
};
|
} else {
|
||||||
|
Err(#error_enum_name::#error_enum_varient(#inner_parse_enum::#error_name))
|
||||||
|
};
|
||||||
|
match parsed {
|
||||||
|
Ok(_) => {
|
||||||
|
#parse_children
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
errors.push(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Dynamic(_, ty) => {
|
Self::Dynamic(name, ty) => {
|
||||||
|
let missing_error_name = self.missing_error_name().unwrap();
|
||||||
quote! {
|
quote! {
|
||||||
let parsed = <#ty as dioxus_router_core::router::FromRouteSegment>::from_route_segment(segment).map_err(|err| #error_enum_name::#error_enum_varient(#inner_parse_enum::#error_name(err)));
|
{
|
||||||
|
let mut segments = segments.clone();
|
||||||
|
let parsed = if let Some(segment) = segments.next() {
|
||||||
|
<#ty as dioxus_router_core::router::FromRouteSegment>::from_route_segment(segment).map_err(|err| #error_enum_name::#error_enum_varient(#inner_parse_enum::#error_name(err)))
|
||||||
|
} else {
|
||||||
|
Err(#error_enum_name::#error_enum_varient(#inner_parse_enum::#missing_error_name))
|
||||||
|
};
|
||||||
|
match parsed {
|
||||||
|
Ok(#name) => {
|
||||||
|
#parse_children
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
errors.push(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::CatchAll(_, ty) => {
|
Self::CatchAll(name, ty) => {
|
||||||
quote! {
|
quote! {
|
||||||
let parsed = <#ty as dioxus_router_core::router::FromRouteSegments>::from_route_segments(segment).map_err(|err| #error_enum_name::#error_enum_varient(#inner_parse_enum::#error_name(err)));
|
{
|
||||||
|
let parsed = {
|
||||||
|
let mut segments = segments.clone();
|
||||||
|
let segments: Vec<_> = segments.collect();
|
||||||
|
<#ty as dioxus_router_core::router::FromRouteSegments>::from_route_segments(&segments).map_err(|err| #error_enum_name::#error_enum_varient(#inner_parse_enum::#error_name(err)))
|
||||||
|
};
|
||||||
|
match parsed {
|
||||||
|
Ok(#name) => {
|
||||||
|
#parse_children
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
errors.push(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,8 @@ pub enum RouteTreeSegment<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RouteTreeSegment<'a> {
|
impl<'a> RouteTreeSegment<'a> {
|
||||||
pub fn build(routes: &'a Vec<Route>) -> Vec<RouteTreeSegment<'a>> {
|
pub fn build(routes: &'a [Route]) -> Vec<RouteTreeSegment<'a>> {
|
||||||
let routes = routes.into_iter().map(PartialRoute::new).collect();
|
let routes = routes.iter().map(PartialRoute::new).collect();
|
||||||
Self::construct(routes)
|
Self::construct(routes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ impl<'a> RouteTreeSegment<'a> {
|
||||||
segment: s,
|
segment: s,
|
||||||
children,
|
children,
|
||||||
..
|
..
|
||||||
} => (s == &segment).then(|| children),
|
} => (s == &segment).then_some(children),
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -89,29 +89,22 @@ impl<'a> RouteTreeSegment<'a> {
|
||||||
let enum_varient = &from_route.route_name;
|
let enum_varient = &from_route.route_name;
|
||||||
let error_ident = static_segment_idx(*index);
|
let error_ident = static_segment_idx(*index);
|
||||||
|
|
||||||
let children_with_next_segment = children.iter().filter_map(|child| match child {
|
let children = children
|
||||||
RouteTreeSegment::StaticEnd { .. } => None,
|
.iter()
|
||||||
_ => Some(child.to_tokens(enum_name.clone(), error_enum_name.clone())),
|
.map(|child| child.to_tokens(enum_name.clone(), error_enum_name.clone()));
|
||||||
});
|
|
||||||
let children_without_next_segment =
|
|
||||||
children.iter().filter_map(|child| match child {
|
|
||||||
RouteTreeSegment::StaticEnd { .. } => {
|
|
||||||
Some(child.to_tokens(enum_name.clone(), error_enum_name.clone()))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
if #segment == segment {
|
{
|
||||||
let mut segments = segments.clone();
|
let mut segments = segments.clone();
|
||||||
#(#children_without_next_segment)*
|
|
||||||
if let Some(segment) = segments.next() {
|
if let Some(segment) = segments.next() {
|
||||||
#(#children_with_next_segment)*
|
if #segment == segment {
|
||||||
|
#(#children)*
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
errors.push(#error_enum_name::#enum_varient(#varient_parse_error::#error_ident))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
errors.push(#error_enum_name::#enum_varient(#varient_parse_error::#error_ident))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RouteTreeSegment::Dynamic(route) => {
|
RouteTreeSegment::Dynamic(route) => {
|
||||||
|
@ -123,50 +116,31 @@ impl<'a> RouteTreeSegment<'a> {
|
||||||
.route_segments
|
.route_segments
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.skip_while(|(_, seg)| match seg {
|
.skip_while(|(_, seg)| matches!(seg, RouteSegment::Static(_)));
|
||||||
RouteSegment::Static(_) => true,
|
|
||||||
_ => false,
|
|
||||||
})
|
|
||||||
.map(|(i, seg)| {
|
|
||||||
(
|
|
||||||
seg.name(),
|
|
||||||
seg.try_parse(i, &error_enum_name, enum_varient, &varient_parse_error),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
fn print_route_segment<I: Iterator<Item = (Option<Ident>, TokenStream)>>(
|
fn print_route_segment<'a, I: Iterator<Item = (usize, &'a RouteSegment)>>(
|
||||||
mut s: std::iter::Peekable<I>,
|
mut s: std::iter::Peekable<I>,
|
||||||
sucess_tokens: TokenStream,
|
sucess_tokens: TokenStream,
|
||||||
|
error_enum_name: &Ident,
|
||||||
|
enum_varient: &Ident,
|
||||||
|
varient_parse_error: &Ident,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
if let Some((name, first)) = s.next() {
|
if let Some((i, route)) = s.next() {
|
||||||
let has_next = s.peek().is_some();
|
let children = print_route_segment(
|
||||||
let children = print_route_segment(s, sucess_tokens);
|
s,
|
||||||
let name = name
|
sucess_tokens,
|
||||||
.map(|name| quote! {#name})
|
error_enum_name,
|
||||||
.unwrap_or_else(|| quote! {_});
|
enum_varient,
|
||||||
|
varient_parse_error,
|
||||||
|
);
|
||||||
|
|
||||||
let sucess = if has_next {
|
route.try_parse(
|
||||||
quote! {
|
i,
|
||||||
let mut segments = segments.clone();
|
error_enum_name,
|
||||||
if let Some(segment) = segments.next() {
|
enum_varient,
|
||||||
#children
|
varient_parse_error,
|
||||||
}
|
children,
|
||||||
}
|
)
|
||||||
} else {
|
|
||||||
children
|
|
||||||
};
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#first
|
|
||||||
match parsed {
|
|
||||||
Ok(#name) => {
|
|
||||||
#sucess
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
errors.push(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
quote! {
|
quote! {
|
||||||
#sucess_tokens
|
#sucess_tokens
|
||||||
|
@ -177,15 +151,25 @@ impl<'a> RouteTreeSegment<'a> {
|
||||||
let construct_variant = route.construct(enum_name);
|
let construct_variant = route.construct(enum_name);
|
||||||
let parse_query = route.parse_query();
|
let parse_query = route.parse_query();
|
||||||
|
|
||||||
|
let insure_not_trailing = route
|
||||||
|
.route_segments
|
||||||
|
.last()
|
||||||
|
.map(|seg| !matches!(seg, RouteSegment::CatchAll(_, _)))
|
||||||
|
.unwrap_or(true);
|
||||||
|
|
||||||
print_route_segment(
|
print_route_segment(
|
||||||
route_segments.peekable(),
|
route_segments.peekable(),
|
||||||
return_constructed(
|
return_constructed(
|
||||||
|
insure_not_trailing,
|
||||||
construct_variant,
|
construct_variant,
|
||||||
&error_enum_name,
|
&error_enum_name,
|
||||||
enum_varient,
|
enum_varient,
|
||||||
&varient_parse_error,
|
&varient_parse_error,
|
||||||
parse_query,
|
parse_query,
|
||||||
),
|
),
|
||||||
|
&error_enum_name,
|
||||||
|
enum_varient,
|
||||||
|
&varient_parse_error,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Self::StaticEnd(route) => {
|
Self::StaticEnd(route) => {
|
||||||
|
@ -195,6 +179,7 @@ impl<'a> RouteTreeSegment<'a> {
|
||||||
let parse_query = route.parse_query();
|
let parse_query = route.parse_query();
|
||||||
|
|
||||||
return_constructed(
|
return_constructed(
|
||||||
|
true,
|
||||||
construct_variant,
|
construct_variant,
|
||||||
&error_enum_name,
|
&error_enum_name,
|
||||||
enum_varient,
|
enum_varient,
|
||||||
|
@ -207,33 +192,41 @@ impl<'a> RouteTreeSegment<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_constructed(
|
fn return_constructed(
|
||||||
|
insure_not_trailing: bool,
|
||||||
construct_variant: TokenStream,
|
construct_variant: TokenStream,
|
||||||
error_enum_name: &Ident,
|
error_enum_name: &Ident,
|
||||||
enum_varient: &Ident,
|
enum_varient: &Ident,
|
||||||
varient_parse_error: &Ident,
|
varient_parse_error: &Ident,
|
||||||
parse_query: TokenStream,
|
parse_query: TokenStream,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
quote! {
|
if insure_not_trailing {
|
||||||
let remaining_segments = segments.clone();
|
quote! {
|
||||||
let mut segments_clone = segments.clone();
|
let remaining_segments = segments.clone();
|
||||||
let next_segment = segments_clone.next();
|
let mut segments_clone = segments.clone();
|
||||||
let segment_after_next = segments_clone.next();
|
let next_segment = segments_clone.next();
|
||||||
match (next_segment, segment_after_next) {
|
let segment_after_next = segments_clone.next();
|
||||||
// This is the last segment, return the parsed route
|
match (next_segment, segment_after_next) {
|
||||||
(None, _) | (Some(""), None) => {
|
// This is the last segment, return the parsed route
|
||||||
#parse_query
|
(None, _) | (Some(""), None) => {
|
||||||
return Ok(#construct_variant);
|
#parse_query
|
||||||
}
|
return Ok(#construct_variant);
|
||||||
_ => {
|
}
|
||||||
let mut trailing = String::new();
|
_ => {
|
||||||
for seg in remaining_segments {
|
let mut trailing = String::new();
|
||||||
trailing += seg;
|
for seg in remaining_segments {
|
||||||
trailing += "/";
|
trailing += seg;
|
||||||
|
trailing += "/";
|
||||||
|
}
|
||||||
|
trailing.pop();
|
||||||
|
errors.push(#error_enum_name::#enum_varient(#varient_parse_error::ExtraSegments(trailing)))
|
||||||
}
|
}
|
||||||
trailing.pop();
|
|
||||||
errors.push(#error_enum_name::#enum_varient(#varient_parse_error::ExtraSegments(trailing)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
#parse_query
|
||||||
|
return Ok(#construct_variant);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue