Implement hash fragments in the router (#2320)

* implement hash fragments in the router

* clean up query and search example

---------

Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
Evan AlmloffandJonathan Kelley authored and GitHub committed 2024-04-25 14:30:25 -04:00
1 parent dd8e69635b
commit 08da9a1253
17 files changed
+550 -190

No files matched your search

+35 -66
View File
@@ -3,7 +3,7 @@ use syn::{Ident, Type};
use proc_macro2::{Span, TokenStream as TokenStream2};
use crate::query::{FullQuerySegment, QueryArgument, QuerySegment};
use crate::{hash::HashFragment, query::QuerySegment};
#[derive(Debug, Clone)]
pub enum RouteSegment {
@@ -24,7 +24,12 @@ impl RouteSegment {
pub fn write_segment(&self) -> TokenStream2 {
match self {
Self::Static(segment) => quote! { write!(f, "/{}", #segment)?; },
Self::Dynamic(ident, _) => quote! { write!(f, "/{}", #ident)?; },
Self::Dynamic(ident, _) => quote! {
{
let as_string = #ident.to_string();
write!(f, "/{}", dioxus_router::exports::urlencoding::encode(&as_string))?;
}
},
Self::CatchAll(ident, _) => quote! { #ident.display_route_segments(f)?; },
}
}
@@ -130,15 +135,38 @@ pub fn static_segment_idx(idx: usize) -> Ident {
pub fn parse_route_segments<'a>(
route_span: Span,
fields: impl Iterator<Item = (&'a Ident, &'a Type)> + Clone,
mut fields: impl Iterator<Item = (&'a Ident, &'a Type)> + Clone,
route: &str,
) -> syn::Result<(Vec<RouteSegment>, Option<QuerySegment>)> {
) -> syn::Result<(
Vec<RouteSegment>,
Option<QuerySegment>,
Option<HashFragment>,
)> {
let mut route_segments = Vec::new();
let (route_string, query) = match route.rsplit_once('?') {
Some((route, query)) => (route, Some(query)),
let (route_string, hash) = match route.rsplit_once('#') {
Some((route, hash)) => (
route,
Some(HashFragment::parse_from_str(
route_span,
fields.clone(),
hash,
)?),
),
None => (route, None),
};
let (route_string, query) = match route_string.rsplit_once('?') {
Some((route, query)) => (
route,
Some(QuerySegment::parse_from_str(
route_span,
fields.clone(),
query,
)?),
),
None => (route_string, None),
};
let mut iterator = route_string.split('/');
// skip the first empty segment
@@ -198,66 +226,7 @@ pub fn parse_route_segments<'a>(
}
}
// check if the route has a query string
let parsed_query = match query {
Some(query) => {
if let Some(query) = query.strip_prefix(":..") {
let query_ident = Ident::new(query, Span::call_site());
let field = fields.clone().find(|(name, _)| *name == &query_ident);
let ty = if let Some((_, ty)) = field {
ty.clone()
} else {
return Err(syn::Error::new(
route_span,
format!("Could not find a field with the name '{}'", query_ident),
));
};
Some(QuerySegment::Single(FullQuerySegment {
ident: query_ident,
ty,
}))
} else {
let mut query_arguments = Vec::new();
for segment in query.split('&') {
if segment.is_empty() {
return Err(syn::Error::new(
route_span,
"Query segments should be non-empty",
));
}
if let Some(query_argument) = segment.strip_prefix(':') {
let query_ident = Ident::new(query_argument, Span::call_site());
let field = fields.clone().find(|(name, _)| *name == &query_ident);
let ty = if let Some((_, ty)) = field {
ty.clone()
} else {
return Err(syn::Error::new(
route_span,
format!("Could not find a field with the name '{}'", query_ident),
));
};
query_arguments.push(QueryArgument {
ident: query_ident,
ty,
});
} else {
return Err(syn::Error::new(
route_span,
"Query segments should be a : followed by the name of the query argument",
));
}
}
Some(QuerySegment::Segments(query_arguments))
}
}
None => None,
};
Ok((route_segments, parsed_query))
Ok((route_segments, query, hash))
}
pub(crate) fn create_error_type(