add static nesting

This commit is contained in:
Evan Almloff committed 2023-05-14 17:39:42 -05:00
1 parent adfc10461e
commit a73873a571
5 files changed
+218 -125

No files matched your search

+115 -2
View File
@@ -1,7 +1,9 @@
use quote::{format_ident, quote};
use syn::{Ident, Type};
use syn::{Ident, Type, Variant};
use proc_macro2::TokenStream as TokenStream2;
use proc_macro2::{Span, TokenStream as TokenStream2};
use crate::query::QuerySegment;
#[derive(Debug)]
pub enum RouteSegment {
@@ -119,3 +121,114 @@ impl RouteSegment {
pub fn static_segment_idx(idx: usize) -> Ident {
format_ident!("StaticSegment{}ParseError", idx)
}
pub fn parse_route_segments(
varient: &Variant,
route: &str,
) -> syn::Result<(Vec<RouteSegment>, Option<QuerySegment>)> {
let mut route_segments = Vec::new();
let (route_string, query) = match route.rsplit_once('?') {
Some((route, query)) => (route, Some(query)),
None => (route, None),
};
let mut iterator = route_string.split('/');
// skip the first empty segment
let first = iterator.next();
if first != Some("") {
return Err(syn::Error::new_spanned(
varient,
format!(
"Routes should start with /. Error found in the route '{}'",
route
),
));
}
while let Some(segment) = iterator.next() {
if segment.starts_with('(') && segment.ends_with(')') {
let spread = segment.starts_with("(...");
let ident = if spread {
segment[4..segment.len() - 1].to_string()
} else {
segment[1..segment.len() - 1].to_string()
};
let field = varient.fields.iter().find(|field| match field.ident {
Some(ref field_ident) => *field_ident == ident,
None => false,
});
let ty = if let Some(field) = field {
field.ty.clone()
} else {
return Err(syn::Error::new_spanned(
varient,
format!(
"Could not find a field with the name '{}' in the variant '{}'",
ident, varient.ident
),
));
};
if spread {
route_segments.push(RouteSegment::CatchAll(
Ident::new(&ident, Span::call_site()),
ty,
));
if iterator.next().is_some() {
return Err(syn::Error::new_spanned(
route,
"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 {
break;
}
} else {
route_segments.push(RouteSegment::Dynamic(
Ident::new(&ident, Span::call_site()),
ty,
));
}
} else {
route_segments.push(RouteSegment::Static(segment.to_string()));
}
}
// check if the route has a query string
let parsed_query = match query {
Some(query) => {
if query.starts_with('(') && query.ends_with(')') {
let query_ident = Ident::new(&query[1..query.len() - 1], Span::call_site());
let field = varient.fields.iter().find(|field| match field.ident {
Some(ref field_ident) => field_ident == &query_ident,
None => false,
});
let ty = if let Some(field) = field {
field.ty.clone()
} else {
return Err(syn::Error::new_spanned(
varient,
format!(
"Could not find a field with the name '{}' in the variant '{}'",
query_ident, varient.ident
),
));
};
Some(QuerySegment {
ident: query_ident,
ty,
})
} else {
None
}
}
None => None,
};
Ok((route_segments, parsed_query))
}