seperate dynamic nested segments and layouts

This commit is contained in:
Evan Almloff committed 2023-05-22 11:44:24 -05:00
1 parent be3dae4b05
commit ef6551a6cd
7 files changed
+294 -330

No files matched your search

+61
View File
@@ -0,0 +1,61 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::Ident;
use crate::nest::{Nest, NestId};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct LayoutId(pub usize);
#[derive(Debug)]
pub struct Layout {
pub layout_name: Ident,
pub comp: Ident,
pub props_name: Ident,
pub active_nests: Vec<NestId>,
}
impl Layout {
pub fn routable_match(&self, nests: &[Nest]) -> TokenStream {
let props_name = &self.props_name;
let comp_name = &self.comp;
let dynamic_segments = self
.active_nests
.iter()
.flat_map(|id| nests[id.0].dynamic_segments());
quote! {
let comp = #props_name { #(#dynamic_segments,)* };
let cx = cx.bump().alloc(Scoped {
props: cx.bump().alloc(comp),
scope: cx,
});
#comp_name(cx)
}
}
}
impl Layout {
pub fn parse(input: syn::parse::ParseStream, active_nests: Vec<NestId>) -> syn::Result<Self> {
// Then parse the layout name
let _ = input.parse::<syn::Token![,]>();
let layout_name: syn::Ident = input.parse()?;
// Then parse the component name
let _ = input.parse::<syn::Token![,]>();
let comp: Ident = input.parse()?;
// Then parse the props name
let _ = input.parse::<syn::Token![,]>();
let props_name: Ident = input
.parse()
.unwrap_or_else(|_| format_ident!("{}Props", comp.to_string()));
Ok(Self {
layout_name,
comp,
props_name,
active_nests,
})
}
}
+78 -66
View File
@@ -1,15 +1,17 @@
extern crate proc_macro; extern crate proc_macro;
use nest::{Layout, Nest}; use layout::Layout;
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 route::Route; use route::Route;
use syn::{parse_macro_input, Ident}; use syn::{parse::ParseStream, parse_macro_input, Ident};
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use crate::{nest::LayoutId, route_tree::RouteTree}; use crate::{layout::LayoutId, route_tree::RouteTree};
mod layout;
mod nest; mod nest;
mod query; mod query;
mod route; mod route;
@@ -46,10 +48,11 @@ pub fn routable(_: TokenStream, input: TokenStream) -> TokenStream {
} }
struct RouteEnum { struct RouteEnum {
vis: syn::Visibility,
attrs: Vec<syn::Attribute>, attrs: Vec<syn::Attribute>,
vis: syn::Visibility,
name: Ident, name: Ident,
routes: Vec<Route>, routes: Vec<Route>,
nests: Vec<Nest>,
layouts: Vec<Layout>, layouts: Vec<Layout>,
} }
@@ -57,79 +60,89 @@ impl RouteEnum {
fn parse(data: syn::ItemEnum) -> syn::Result<Self> { fn parse(data: syn::ItemEnum) -> syn::Result<Self> {
let name = &data.ident; let name = &data.ident;
enum NestRef {
Static(String),
Dynamic { id: LayoutId },
}
let mut routes = Vec::new(); let mut routes = Vec::new();
let mut layouts = Vec::new(); let mut layouts = Vec::new();
let mut layout_stack = Vec::new();
let mut nests = Vec::new();
let mut nest_stack = Vec::new(); let mut nest_stack = Vec::new();
for variant in data.variants { for variant in &data.variants {
// Apply the any nesting attributes in order // Apply the any nesting attributes in order
for attr in &variant.attrs { for attr in &variant.attrs {
if attr.path.is_ident("nest") { if attr.path.is_ident("nest") {
let nest: Nest = attr.parse_args()?; let mut children_routes = Vec::new();
let nest_ref = match nest { {
Nest::Static(s) => NestRef::Static(s), // add all of the variants of the enum to the children_routes until we hit an end_nest
Nest::Layout(mut l) => { let mut level = 0;
// if there is a static nest before this, add it to the layout 'o: for variant in &data.variants {
let mut static_prefix = nest_stack children_routes.push(variant.fields.clone());
.iter() for attr in &variant.attrs {
// walk backwards and take all static nests if attr.path.is_ident("nest") {
.rev() level += 1;
.map_while(|nest| match nest { } else if attr.path.is_ident("end_nest") {
NestRef::Static(s) => Some(s.clone()), level -= 1;
NestRef::Dynamic { .. } => None, if level < 0 {
}) break 'o;
.collect::<Vec<_>>(); }
// reverse the static prefix so it is in the correct order }
static_prefix.reverse();
if !static_prefix.is_empty() {
l.add_static_prefix(&static_prefix.join("/"));
} }
let id = layouts.len();
layouts.push(l);
NestRef::Dynamic { id: LayoutId(id) }
} }
}
let nest_index = nests.len();
let parser = |input: ParseStream| {
Nest::parse(
input,
children_routes
.iter()
.filter_map(|f: &syn::Fields| match f {
syn::Fields::Named(fields) => Some(fields.clone()),
_ => None,
})
.collect(),
nest_index,
)
}; };
nest_stack.push(nest_ref); let nest = attr.parse_args_with(parser)?;
nests.push(nest);
nest_stack.push(NestId(nest_index));
} else if attr.path.is_ident("end_nest") { } else if attr.path.is_ident("end_nest") {
nest_stack.pop(); nest_stack.pop();
} else if attr.path.is_ident("layout") {
let layout_index = layouts.len();
let parser = |input: ParseStream| {
Layout::parse(input, nest_stack.iter().rev().cloned().collect())
};
let layout = attr.parse_args_with(parser)?;
layouts.push(layout);
layout_stack.push(LayoutId(layout_index));
} else if attr.path.is_ident("end_layout") {
layout_stack.pop();
} }
} }
let mut trailing_static_route = nest_stack let mut active_nests = nest_stack.clone();
.iter() active_nests.reverse();
.rev() let mut active_layouts = layout_stack.clone();
.map_while(|nest| match nest { active_layouts.reverse();
NestRef::Static(s) => Some(s.clone()),
NestRef::Dynamic { .. } => None, let route = Route::parse(active_nests, active_layouts, variant.clone())?;
})
.collect::<Vec<_>>();
trailing_static_route.reverse();
let active_layouts = nest_stack
.iter()
.filter_map(|nest| match nest {
NestRef::Static(_) => None,
NestRef::Dynamic { id } => Some(*id),
})
.collect::<Vec<_>>();
let route = Route::parse(trailing_static_route.join("/"), active_layouts, variant)?;
routes.push(route); routes.push(route);
} }
let myself = Self { let myself = Self {
vis: data.vis,
attrs: data.attrs,
name: name.clone(), name: name.clone(),
attrs: data.attrs,
vis: data.vis,
routes, routes,
nests,
layouts, layouts,
}; };
@@ -140,7 +153,7 @@ impl RouteEnum {
let mut display_match = Vec::new(); let mut display_match = Vec::new();
for route in &self.routes { for route in &self.routes {
display_match.push(route.display_match(&self.layouts)); display_match.push(route.display_match(&self.nests));
} }
let name = &self.name; let name = &self.name;
@@ -158,13 +171,13 @@ impl RouteEnum {
} }
fn parse_impl(&self) -> TokenStream2 { fn parse_impl(&self) -> TokenStream2 {
let tree = RouteTree::new(&self.routes, &self.layouts); let tree = RouteTree::new(&self.routes, &self.nests);
let name = &self.name; let name = &self.name;
let error_name = format_ident!("{}MatchError", self.name); let error_name = format_ident!("{}MatchError", self.name);
let tokens = tree.roots.iter().map(|&id| { let tokens = tree.roots.iter().map(|&id| {
let route = tree.get(id).unwrap(); let route = tree.get(id).unwrap();
route.to_tokens(&tree, self.name.clone(), error_name.clone(), &self.layouts) route.to_tokens(&tree, self.name.clone(), error_name.clone())
}); });
quote! { quote! {
@@ -217,15 +230,14 @@ impl RouteEnum {
type_defs.push(route.error_type()); type_defs.push(route.error_type());
} }
for layout in &self.layouts { for nest in &self.nests {
let layout_name = &layout.layout_name; let error_variant = nest.error_variant();
let error_name = nest.error_ident();
let route_str = &nest.route;
let error_name = layout.error_ident(); error_variants.push(quote! { #error_variant(#error_name) });
let route_str = &layout.route; display_match.push(quote! { Self::#error_variant(err) => write!(f, "Nest '{}' ('{}') did not match:\n{}", stringify!(#error_name), #route_str, err)? });
type_defs.push(nest.error_type());
error_variants.push(quote! { #layout_name(#error_name) });
display_match.push(quote! { Self::#layout_name(err) => write!(f, "Layout '{}' ('{}') did not match:\n{}", stringify!(#layout_name), #route_str, err)? });
type_defs.push(layout.error_type());
} }
quote! { quote! {
@@ -259,7 +271,7 @@ impl RouteEnum {
// Collect all routes that match the current layer // Collect all routes that match the current layer
for route in &self.routes { for route in &self.routes {
if let Some(matched) = route.routable_match(&self.layouts, index) { if let Some(matched) = route.routable_match(&self.layouts, &self.nests, index) {
routable_match.push(matched); routable_match.push(matched);
} }
} }
@@ -303,7 +315,7 @@ impl ToTokens for RouteEnum {
let vis = &self.vis; let vis = &self.vis;
let name = &self.name; let name = &self.name;
let attrs = &self.attrs; let attrs = &self.attrs;
let variants = routes.iter().map(|r| r.variant(&self.layouts)); let variants = routes.iter().map(|r| r.variant());
tokens.extend(quote!( tokens.extend(quote!(
#(#attrs)* #(#attrs)*
+46 -96
View File
@@ -1,83 +1,55 @@
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use syn::{parse::Parse, Ident, LitStr}; use syn::{Ident, LitStr};
use crate::segment::{parse_route_segments, RouteSegment}; use crate::segment::{parse_route_segments, RouteSegment};
pub enum Nest { #[derive(Debug, Clone, Copy)]
Static(String), pub struct NestId(pub usize);
Layout(Layout),
}
impl Parse for Nest { #[derive(Debug, Clone)]
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { pub struct Nest {
// First parse the route
let route: LitStr = input.parse()?;
let is_dynamic = route.value().contains('(');
if !input.is_empty() || is_dynamic {
// Then parse the layout name
let _ = input.parse::<syn::Token![,]>();
let layout_name: syn::Ident = input.parse()?;
let layout_fields: syn::FieldsNamed = input.parse()?;
// Then parse the component name
let _ = input.parse::<syn::Token![,]>();
let comp: Ident = input.parse()?;
// Then parse the props name
let _ = input.parse::<syn::Token![,]>();
let props_name: Ident = input
.parse()
.unwrap_or_else(|_| format_ident!("{}Props", comp.to_string()));
let route_segments =
parse_route_segments(&layout_name, &layout_fields, &route.value())?.0;
for seg in &route_segments {
if let RouteSegment::CatchAll(name, _) = seg {
return Err(syn::Error::new_spanned(
name,
format!(
"Catch-all segments are not allowed in nested routes: {}",
route.value()
),
));
}
}
Ok(Self::Layout(Layout {
route: route.value(),
segments: route_segments,
layout_name,
comp,
props_name,
layout_fields,
}))
} else {
Ok(Self::Static(route.value()))
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct LayoutId(pub usize);
#[derive(Debug)]
pub struct Layout {
pub route: String, pub route: String,
pub segments: Vec<RouteSegment>, pub segments: Vec<RouteSegment>,
pub layout_name: Ident, index: usize,
pub layout_fields: syn::FieldsNamed,
pub comp: Ident,
pub props_name: Ident,
} }
impl Layout { impl Nest {
pub fn add_static_prefix(&mut self, prefix: &str) { pub fn parse(
self.route = format!("{}{}", prefix, self.route); input: syn::parse::ParseStream,
self.segments.push(RouteSegment::Static(prefix.to_string())); children_routes: Vec<syn::FieldsNamed>,
} index: usize,
) -> syn::Result<Self> {
// Parse the route
let route: LitStr = input.parse()?;
let route_segments = parse_route_segments(
route.span(),
children_routes.iter().flat_map(|f| f.named.iter()),
&route.value(),
)?
.0;
for seg in &route_segments {
if let RouteSegment::CatchAll(name, _) = seg {
return Err(syn::Error::new_spanned(
name,
format!(
"Catch-all segments are not allowed in nested routes: {}",
route.value()
),
));
}
}
Ok(Self {
route: route.value(),
segments: route_segments,
index,
})
}
}
impl Nest {
pub fn dynamic_segments(&self) -> impl Iterator<Item = TokenStream> + '_ { pub fn dynamic_segments(&self) -> impl Iterator<Item = TokenStream> + '_ {
self.segments self.segments
.iter() .iter()
@@ -85,13 +57,6 @@ impl Layout {
.map(|i| quote! {#i}) .map(|i| quote! {#i})
} }
pub fn dynamic_segment_types(&self) -> impl Iterator<Item = TokenStream> + '_ {
self.segments
.iter()
.filter_map(|seg| seg.ty())
.map(|ty| quote! {#ty})
}
pub fn write(&self) -> TokenStream { pub fn write(&self) -> TokenStream {
let write_segments = self.segments.iter().map(|s| s.write_segment()); let write_segments = self.segments.iter().map(|s| s.write_segment());
@@ -103,7 +68,11 @@ impl Layout {
} }
pub fn error_ident(&self) -> Ident { pub fn error_ident(&self) -> Ident {
format_ident!("{}LayoutParseError", self.layout_name) format_ident!("Nest{}ParseError", self.index)
}
pub fn error_variant(&self) -> Ident {
format_ident!("Nest{}", self.index)
} }
pub fn error_type(&self) -> TokenStream { pub fn error_type(&self) -> TokenStream {
@@ -147,23 +116,4 @@ impl Layout {
} }
} }
} }
pub fn routable_match(&self) -> TokenStream {
let props_name = &self.props_name;
let comp_name = &self.comp;
let dynamic_segments_from_route = self
.segments
.iter()
.filter_map(|seg| seg.name())
.map(|seg| quote! { #seg });
quote! {
let comp = #props_name { #(#dynamic_segments_from_route,)* };
let cx = cx.bump().alloc(Scoped {
props: cx.bump().alloc(comp),
scope: cx,
});
#comp_name(cx)
}
}
} }
-8
View File
@@ -24,12 +24,4 @@ impl QuerySegment {
write!(f, "?{}", #ident)?; write!(f, "?{}", #ident)?;
} }
} }
pub fn name(&self) -> Ident {
self.ident.clone()
}
pub fn ty(&self) -> &Type {
&self.ty
}
} }
+43 -80
View File
@@ -5,8 +5,10 @@ use syn::{Ident, LitStr};
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use crate::nest::Layout; use crate::layout::Layout;
use crate::nest::LayoutId; use crate::layout::LayoutId;
use crate::nest::Nest;
use crate::nest::NestId;
use crate::query::QuerySegment; use crate::query::QuerySegment;
use crate::segment::parse_route_segments; use crate::segment::parse_route_segments;
use crate::segment::RouteSegment; use crate::segment::RouteSegment;
@@ -44,13 +46,15 @@ pub struct Route {
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 layouts: Vec<LayoutId>, pub layouts: Vec<LayoutId>,
pub variant: syn::Variant, pub variant: syn::Variant,
fields: syn::FieldsNamed,
} }
impl Route { impl Route {
pub fn parse( pub fn parse(
root_route: String, nests: Vec<NestId>,
layouts: Vec<LayoutId>, layouts: Vec<LayoutId>,
variant: syn::Variant, variant: syn::Variant,
) -> syn::Result<Self> { ) -> syn::Result<Self> {
@@ -67,7 +71,7 @@ impl Route {
let route_name = variant.ident.clone(); let route_name = variant.ident.clone();
let args = route_attr.parse_args::<RouteArgs>()?; let args = route_attr.parse_args::<RouteArgs>()?;
let route = root_route + &args.route.value(); let route = args.route.value();
let file_based = args.comp_name.is_none(); let file_based = args.comp_name.is_none();
let comp_name = args let comp_name = args
.comp_name .comp_name
@@ -86,7 +90,8 @@ impl Route {
} }
}; };
let (route_segments, query) = parse_route_segments(&variant.ident, named_fields, &route)?; let (route_segments, query) =
parse_route_segments(variant.ident.span(), named_fields.named.iter(), &route)?;
Ok(Self { Ok(Self {
comp_name, comp_name,
@@ -96,15 +101,17 @@ impl Route {
route, route,
file_based, file_based,
query, query,
nests,
layouts, layouts,
fields: named_fields.clone(),
variant, variant,
}) })
} }
pub fn display_match(&self, layouts: &[Layout]) -> TokenStream2 { pub fn display_match(&self, nests: &[Nest]) -> TokenStream2 {
let name = &self.route_name; let name = &self.route_name;
let dynamic_segments = self.dynamic_segments(layouts); let dynamic_segments = self.dynamic_segments();
let write_layouts = self.layouts.iter().map(|id| layouts[id.0].write()); 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_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());
@@ -117,14 +124,19 @@ impl Route {
} }
} }
pub fn routable_match(&self, layouts: &[Layout], index: usize) -> Option<TokenStream2> { pub fn routable_match(
&self,
layouts: &[Layout],
nests: &[Nest],
index: usize,
) -> Option<TokenStream2> {
let name = &self.route_name; let name = &self.route_name;
let dynamic_segments = self.dynamic_segments(layouts); let dynamic_segments = self.dynamic_segments();
match index.cmp(&self.layouts.len()) { match index.cmp(&self.layouts.len()) {
std::cmp::Ordering::Less => { std::cmp::Ordering::Less => {
let layout = self.layouts[index]; let layout = self.layouts[index];
let render_layout = layouts[layout.0].routable_match(); let render_layout = layouts[layout.0].routable_match(nests);
// This is a layout // This is a layout
Some(quote! { Some(quote! {
#[allow(unused)] #[allow(unused)]
@@ -134,7 +146,7 @@ impl Route {
}) })
} }
std::cmp::Ordering::Equal => { std::cmp::Ordering::Equal => {
let dynamic_segments_from_route = self.dynamic_segments_from_route(); let dynamic_segments_from_route = self.dynamic_segments();
let props_name = &self.props_name; let props_name = &self.props_name;
let comp_name = &self.comp_name; let comp_name = &self.comp_name;
// This is the final route // This is the final route
@@ -154,72 +166,15 @@ impl Route {
} }
} }
fn dynamic_segment_types<'a>( fn dynamic_segments(&self) -> impl Iterator<Item = TokenStream2> + '_ {
&'a self, self.fields.named.iter().map(|f| {
layouts: &'a [Layout], let name = f.ident.as_ref().unwrap();
) -> impl Iterator<Item = TokenStream2> + 'a { quote! {#name}
let layouts = self })
.layouts
.iter()
.flat_map(|id| layouts[id.0].dynamic_segment_types());
let segments = self.segments.iter().filter_map(|seg| {
let ty = seg.ty()?;
Some(quote! {
#ty
})
});
let query = self
.query
.as_ref()
.map(|q| {
let ty = q.ty();
quote! {
#ty
}
})
.into_iter();
layouts.chain(segments.chain(query))
} }
fn dynamic_segments_from_route(&self) -> impl Iterator<Item = TokenStream2> + '_ { pub fn construct(&self, enum_name: Ident) -> TokenStream2 {
let segments = self.segments.iter().filter_map(|seg| { let segments = self.dynamic_segments();
seg.name().map(|name| {
quote! {
#name
}
})
});
let query = self
.query
.as_ref()
.map(|q| {
let name = q.name();
quote! {
#name
}
})
.into_iter();
segments.chain(query)
}
fn dynamic_segments<'a>(
&'a self,
layouts: &'a [Layout],
) -> impl Iterator<Item = TokenStream2> + 'a {
let layouts = self
.layouts
.iter()
.flat_map(|id| layouts[id.0].dynamic_segments());
let dynamic_segments = self.dynamic_segments_from_route();
layouts.chain(dynamic_segments)
}
pub fn construct(&self, enum_name: Ident, layouts: &[Layout]) -> TokenStream2 {
let segments = self.dynamic_segments(layouts);
let name = &self.route_name; let name = &self.route_name;
quote! { quote! {
@@ -289,13 +244,21 @@ impl Route {
} }
} }
pub fn variant(&self, layouts: &[Layout]) -> TokenStream2 { pub fn variant(&self) -> TokenStream2 {
let name = &self.route_name; let name = &self.route_name;
let segments = self.dynamic_segments(layouts); let fields = self.fields.named.iter().map(|f| {
let types = self.dynamic_segment_types(layouts); let mut new = f.clone();
new.attrs.retain(|a| {
!a.path.is_ident("nest")
&& !a.path.is_ident("end_nest")
&& !a.path.is_ident("layout")
&& !a.path.is_ident("end_layout")
});
new
});
quote! { quote! {
#name { #(#segments: #types,)* } #name { #(#fields,)* }
} }
} }
} }
+50 -50
View File
@@ -4,7 +4,7 @@ use slab::Slab;
use syn::Ident; use syn::Ident;
use crate::{ use crate::{
nest::Layout, nest::Nest,
route::Route, route::Route,
segment::{static_segment_idx, RouteSegment}, segment::{static_segment_idx, RouteSegment},
}; };
@@ -39,7 +39,7 @@ impl<'a> RouteTree<'a> {
let seg = self.get(seg).unwrap(); let seg = self.get(seg).unwrap();
match seg { match seg {
RouteTreeSegmentData::Static { .. } => 0, RouteTreeSegmentData::Static { .. } => 0,
RouteTreeSegmentData::Layout { .. } => 1, RouteTreeSegmentData::Nest { .. } => 1,
RouteTreeSegmentData::Route(route) => { RouteTreeSegmentData::Route(route) => {
// Routes that end in a catch all segment should be checked last // Routes that end in a catch all segment should be checked last
match route.segments.last() { match route.segments.last() {
@@ -70,7 +70,7 @@ impl<'a> RouteTree<'a> {
let element = self.entries.get(element).unwrap(); let element = self.entries.get(element).unwrap();
match element { match element {
RouteTreeSegmentData::Static { children, .. } => children.clone(), RouteTreeSegmentData::Static { children, .. } => children.clone(),
RouteTreeSegmentData::Layout { children, .. } => children.clone(), RouteTreeSegmentData::Nest { children, .. } => children.clone(),
_ => Vec::new(), _ => Vec::new(),
} }
} }
@@ -79,20 +79,20 @@ impl<'a> RouteTree<'a> {
let element = self.entries.get_mut(element).unwrap(); let element = self.entries.get_mut(element).unwrap();
match element { match element {
RouteTreeSegmentData::Static { children, .. } => Some(children), RouteTreeSegmentData::Static { children, .. } => Some(children),
RouteTreeSegmentData::Layout { children, .. } => Some(children), RouteTreeSegmentData::Nest { children, .. } => Some(children),
_ => None, _ => None,
} }
} }
fn children_mut(&mut self, element: usize) -> &mut Vec<usize> { fn children_mut(&mut self, element: usize) -> &mut Vec<usize> {
self.try_children_mut(element) self.try_children_mut(element)
.expect("Cannot get children of non static or layout segment") .expect("Cannot get children of non static or nest segment")
} }
pub fn new(routes: &'a [Route], layouts: &'a [Layout]) -> Self { pub fn new(routes: &'a [Route], nests: &'a [Nest]) -> Self {
let routes = routes let routes = routes
.iter() .iter()
.map(|route| RouteIter::new(route, layouts)) .map(|route| RouteIter::new(route, nests))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let mut myself = Self::default(); let mut myself = Self::default();
@@ -109,11 +109,11 @@ impl<'a> RouteTree<'a> {
for mut route in routes { for mut route in routes {
let mut current_route: Option<usize> = None; let mut current_route: Option<usize> = None;
// First add a layout if there is one // First add all nests
while let Some(layout) = route.next_layout() { while let Some(nest) = route.next_nest() {
let segments_iter: std::slice::Iter<RouteSegment> = layout.segments.iter(); let segments_iter = nest.segments.iter();
// Add all static segments of the layout // Add all static segments of the nest
'o: for (index, segment) in segments_iter.enumerate() { 'o: for (index, segment) in segments_iter.enumerate() {
match segment { match segment {
RouteSegment::Static(segment) => { RouteSegment::Static(segment) => {
@@ -124,17 +124,12 @@ impl<'a> RouteTree<'a> {
.map(|id| self.children(id)) .map(|id| self.children(id))
.unwrap_or_else(|| segments.clone()); .unwrap_or_else(|| segments.clone());
for seg in segments.iter() { for &seg_id in segments.iter() {
let seg = self.get(*seg).unwrap(); let seg = self.get(seg_id).unwrap();
if let RouteTreeSegmentData::Static { if let RouteTreeSegmentData::Static { segment: s, .. } = seg {
segment: s,
children,
..
} = seg
{
if s == segment { if s == segment {
// If it does, just update the current route // If it does, just update the current route
current_route = children.last().cloned(); current_route = Some(seg_id);
continue 'o; continue 'o;
} }
} }
@@ -144,7 +139,10 @@ impl<'a> RouteTree<'a> {
let static_segment = RouteTreeSegmentData::Static { let static_segment = RouteTreeSegmentData::Static {
segment, segment,
children: Vec::new(), children: Vec::new(),
error_variant: route.error_variant(), error_variant: StaticErrorVariant {
varient_parse_error: nest.error_ident(),
enum_varient: nest.error_variant(),
},
index, index,
}; };
@@ -155,28 +153,31 @@ impl<'a> RouteTree<'a> {
.map(|id| self.children_mut(id)) .map(|id| self.children_mut(id))
.unwrap_or_else(|| &mut segments); .unwrap_or_else(|| &mut segments);
current_children.push(static_segment); current_children.push(static_segment);
// Update the current route
current_route = Some(static_segment);
} }
// If there is a dynamic segment, stop adding static segments // If there is a dynamic segment, stop adding static segments
RouteSegment::Dynamic(..) => break, RouteSegment::Dynamic(..) => break,
RouteSegment::CatchAll(..) => { RouteSegment::CatchAll(..) => {
todo!("Catch all segments are not allowed in layouts") todo!("Catch all segments are not allowed in nests")
} }
} }
} }
// Add the layout to the current route // Add the nest to the current route
let layout = RouteTreeSegmentData::Layout { let nest = RouteTreeSegmentData::Nest {
layout, nest,
children: Vec::new(), children: Vec::new(),
}; };
let layout = self.entries.insert(layout); let nest = self.entries.insert(nest);
let segments = match current_route.and_then(|id| self.get_mut(id)) { let segments = match current_route.and_then(|id| self.get_mut(id)) {
Some(RouteTreeSegmentData::Static { children, .. }) => children, Some(RouteTreeSegmentData::Static { children, .. }) => children,
Some(_) => unreachable!(), Some(r) => unreachable!("{r:?} is not a static segment"),
None => &mut segments, None => &mut segments,
}; };
segments.push(layout); segments.push(nest);
// Update the current route // Update the current route
current_route = segments.last().cloned(); current_route = segments.last().cloned();
@@ -252,8 +253,8 @@ pub enum RouteTreeSegmentData<'a> {
index: usize, index: usize,
children: Vec<usize>, children: Vec<usize>,
}, },
Layout { Nest {
layout: &'a Layout, nest: &'a Nest,
children: Vec<usize>, children: Vec<usize>,
}, },
Route(&'a Route), Route(&'a Route),
@@ -265,7 +266,6 @@ impl<'a> RouteTreeSegmentData<'a> {
tree: &RouteTree, tree: &RouteTree,
enum_name: syn::Ident, enum_name: syn::Ident,
error_enum_name: syn::Ident, error_enum_name: syn::Ident,
layouts: &[Layout],
) -> TokenStream { ) -> TokenStream {
match self { match self {
RouteTreeSegmentData::Static { RouteTreeSegmentData::Static {
@@ -282,7 +282,7 @@ impl<'a> RouteTreeSegmentData<'a> {
let children = children.iter().map(|child| { let children = children.iter().map(|child| {
let child = tree.get(*child).unwrap(); let child = tree.get(*child).unwrap();
child.to_tokens(tree, enum_name.clone(), error_enum_name.clone(), layouts) child.to_tokens(tree, enum_name.clone(), error_enum_name.clone())
}); });
quote! { quote! {
@@ -310,7 +310,7 @@ impl<'a> RouteTreeSegmentData<'a> {
.enumerate() .enumerate()
.skip_while(|(_, seg)| matches!(seg, RouteSegment::Static(_))); .skip_while(|(_, seg)| matches!(seg, RouteSegment::Static(_)));
let construct_variant = route.construct(enum_name, layouts); let construct_variant = route.construct(enum_name);
let parse_query = route.parse_query(); let parse_query = route.parse_query();
let insure_not_trailing = route let insure_not_trailing = route
@@ -334,12 +334,12 @@ impl<'a> RouteTreeSegmentData<'a> {
&varient_parse_error, &varient_parse_error,
) )
} }
Self::Layout { layout, children } => { Self::Nest { nest, children } => {
// At this point, we have matched all static segments, so we can just check if the remaining segments match the route // At this point, we have matched all static segments, so we can just check if the remaining segments match the route
let varient_parse_error: Ident = layout.error_ident(); let varient_parse_error: Ident = nest.error_ident();
let enum_varient = &layout.layout_name; let enum_varient = nest.error_variant();
let route_segments = layout let route_segments = nest
.segments .segments
.iter() .iter()
.enumerate() .enumerate()
@@ -349,7 +349,7 @@ impl<'a> RouteTreeSegmentData<'a> {
.iter() .iter()
.map(|child| { .map(|child| {
let child = tree.get(*child).unwrap(); let child = tree.get(*child).unwrap();
child.to_tokens(tree, enum_name.clone(), error_enum_name.clone(), layouts) child.to_tokens(tree, enum_name.clone(), error_enum_name.clone())
}) })
.collect(); .collect();
@@ -357,7 +357,7 @@ impl<'a> RouteTreeSegmentData<'a> {
route_segments.peekable(), route_segments.peekable(),
parse_children, parse_children,
&error_enum_name, &error_enum_name,
enum_varient, &enum_varient,
&varient_parse_error, &varient_parse_error,
) )
} }
@@ -436,27 +436,27 @@ fn return_constructed(
pub struct RouteIter<'a> { pub struct RouteIter<'a> {
route: &'a Route, route: &'a Route,
layouts: &'a [Layout], nests: &'a [Nest],
layout_index: usize, nest_index: usize,
static_segment_index: usize, static_segment_index: usize,
} }
impl<'a> RouteIter<'a> { impl<'a> RouteIter<'a> {
fn new(route: &'a Route, layouts: &'a [Layout]) -> Self { fn new(route: &'a Route, nests: &'a [Nest]) -> Self {
Self { Self {
route, route,
layouts, nests,
layout_index: 0, nest_index: 0,
static_segment_index: 0, static_segment_index: 0,
} }
} }
fn next_layout(&mut self) -> Option<&'a Layout> { fn next_nest(&mut self) -> Option<&'a Nest> {
let idx = self.layout_index; let idx = self.nest_index;
let layout_index = self.route.layouts.get(idx)?; let nest_index = self.route.nests.get(idx)?;
let layout = &self.layouts[layout_index.0]; let nest = &self.nests[nest_index.0];
self.layout_index += 1; self.nest_index += 1;
Some(layout) Some(nest)
} }
fn next_static_segment(&mut self) -> Option<(usize, &'a str)> { fn next_static_segment(&mut self) -> Option<(usize, &'a str)> {
+16 -30
View File
@@ -5,7 +5,7 @@ use proc_macro2::{Span, TokenStream as TokenStream2};
use crate::query::QuerySegment; use crate::query::QuerySegment;
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum RouteSegment { pub enum RouteSegment {
Static(String), Static(String),
Dynamic(Ident, Type), Dynamic(Ident, Type),
@@ -21,14 +21,6 @@ impl RouteSegment {
} }
} }
pub fn ty(&self) -> Option<&Type> {
match self {
Self::Static(_) => None,
Self::Dynamic(_, ty) => Some(ty),
Self::CatchAll(_, ty) => Some(ty),
}
}
pub fn write_segment(&self) -> TokenStream2 { pub fn write_segment(&self) -> TokenStream2 {
match self { match self {
Self::Static(segment) => quote! { write!(f, "/{}", #segment)?; }, Self::Static(segment) => quote! { write!(f, "/{}", #segment)?; },
@@ -130,9 +122,9 @@ pub fn static_segment_idx(idx: usize) -> Ident {
format_ident!("StaticSegment{}ParseError", idx) format_ident!("StaticSegment{}ParseError", idx)
} }
pub fn parse_route_segments( pub fn parse_route_segments<'a>(
route_name: &Ident, route_span: Span,
fields: &syn::FieldsNamed, mut fields: impl Iterator<Item = &'a syn::Field>,
route: &str, route: &str,
) -> syn::Result<(Vec<RouteSegment>, Option<QuerySegment>)> { ) -> syn::Result<(Vec<RouteSegment>, Option<QuerySegment>)> {
let mut route_segments = Vec::new(); let mut route_segments = Vec::new();
@@ -146,8 +138,8 @@ pub fn parse_route_segments(
// skip the first empty segment // skip the first empty segment
let first = iterator.next(); let first = iterator.next();
if first != Some("") { if first != Some("") {
return Err(syn::Error::new_spanned( return Err(syn::Error::new(
route_name, route_span,
format!( format!(
"Routes should start with /. Error found in the route '{}'", "Routes should start with /. Error found in the route '{}'",
route route
@@ -165,7 +157,7 @@ pub fn parse_route_segments(
segment.to_string() segment.to_string()
}; };
let field = fields.named.iter().find(|field| match field.ident { let field = fields.find(|field| match field.ident {
Some(ref field_ident) => *field_ident == ident, Some(ref field_ident) => *field_ident == ident,
None => false, None => false,
}); });
@@ -173,12 +165,9 @@ pub fn parse_route_segments(
let ty = if let Some(field) = field { let ty = if let Some(field) = field {
field.ty.clone() field.ty.clone()
} else { } else {
return Err(syn::Error::new_spanned( return Err(syn::Error::new(
route_name, route_span,
format!( format!("Could not find a field with the name '{}'", ident,),
"Could not find a field with the name '{}' in the variant '{}'",
ident, route_name
),
)); ));
}; };
if spread { if spread {
@@ -188,8 +177,8 @@ pub fn parse_route_segments(
)); ));
if iterator.next().is_some() { if iterator.next().is_some() {
return Err(syn::Error::new_spanned( return Err(syn::Error::new(
route, route_span,
"Catch-all route segments must be the last segment in a route. The route segments after the catch-all segment will never be matched.", "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 { } else {
@@ -211,7 +200,7 @@ pub fn parse_route_segments(
Some(query) => { Some(query) => {
if let Some(query) = query.strip_prefix(':') { if let Some(query) = query.strip_prefix(':') {
let query_ident = Ident::new(query, Span::call_site()); let query_ident = Ident::new(query, Span::call_site());
let field = fields.named.iter().find(|field| match field.ident { let field = fields.find(|field| match field.ident {
Some(ref field_ident) => field_ident == &query_ident, Some(ref field_ident) => field_ident == &query_ident,
None => false, None => false,
}); });
@@ -219,12 +208,9 @@ pub fn parse_route_segments(
let ty = if let Some(field) = field { let ty = if let Some(field) = field {
field.ty.clone() field.ty.clone()
} else { } else {
return Err(syn::Error::new_spanned( return Err(syn::Error::new(
route_name, route_span,
format!( format!("Could not find a field with the name '{}'", query_ident),
"Could not find a field with the name '{}' in the variant '{}'",
query_ident, route_name
),
)); ));
}; };