generate site map constant
This commit is contained in:
1 parent
502d670dff
commit
d0d7e88a0d
3 files changed
+183
-3
No files matched your search
@@ -5,6 +5,7 @@ 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 segment::RouteSegment;
|
||||||
use syn::{parse::ParseStream, parse_macro_input, Ident, Token};
|
use syn::{parse::ParseStream, parse_macro_input, Ident, Token};
|
||||||
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
@@ -51,12 +52,16 @@ struct RouteEnum {
|
|||||||
routes: Vec<Route>,
|
routes: Vec<Route>,
|
||||||
nests: Vec<Nest>,
|
nests: Vec<Nest>,
|
||||||
layouts: Vec<Layout>,
|
layouts: Vec<Layout>,
|
||||||
|
site_map: Vec<SiteMapSegment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RouteEnum {
|
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;
|
||||||
|
|
||||||
|
let mut site_map = Vec::new();
|
||||||
|
let mut site_map_stack: Vec<Vec<SiteMapSegment>> = Vec::new();
|
||||||
|
|
||||||
let mut routes = Vec::new();
|
let mut routes = Vec::new();
|
||||||
|
|
||||||
let mut layouts: Vec<Layout> = Vec::new();
|
let mut layouts: Vec<Layout> = Vec::new();
|
||||||
@@ -106,10 +111,43 @@ impl RouteEnum {
|
|||||||
};
|
};
|
||||||
let nest = attr.parse_args_with(parser)?;
|
let nest = attr.parse_args_with(parser)?;
|
||||||
|
|
||||||
|
// add the current segment to the site map stack
|
||||||
|
let segments: Vec<_> = nest
|
||||||
|
.segments
|
||||||
|
.iter()
|
||||||
|
.map(|seg| {
|
||||||
|
let segment_type = seg.into();
|
||||||
|
SiteMapSegment {
|
||||||
|
segment_type,
|
||||||
|
children: Vec::new(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
if !segments.is_empty() {
|
||||||
|
site_map_stack.push(segments);
|
||||||
|
}
|
||||||
|
|
||||||
nests.push(nest);
|
nests.push(nest);
|
||||||
nest_stack.push(NestId(nest_index));
|
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();
|
||||||
|
// pop the current nest segment off the stack and add it to the parent or the site map
|
||||||
|
if let Some(segment) = site_map_stack.pop() {
|
||||||
|
let children = site_map_stack
|
||||||
|
.last_mut()
|
||||||
|
.map(|seg| &mut seg.last_mut().unwrap().children)
|
||||||
|
.unwrap_or(&mut site_map);
|
||||||
|
|
||||||
|
// Turn the list of segments in the segments stack into a tree
|
||||||
|
let mut iter = segment.into_iter().rev();
|
||||||
|
let mut current = iter.next().unwrap();
|
||||||
|
for mut segment in iter {
|
||||||
|
segment.children.push(current);
|
||||||
|
current = segment;
|
||||||
|
}
|
||||||
|
|
||||||
|
children.push(current);
|
||||||
|
}
|
||||||
} else if attr.path.is_ident("layout") {
|
} else if attr.path.is_ident("layout") {
|
||||||
let parser = |input: ParseStream| {
|
let parser = |input: ParseStream| {
|
||||||
let bang: Option<Token![!]> = input.parse().ok();
|
let bang: Option<Token![!]> = input.parse().ok();
|
||||||
@@ -149,6 +187,16 @@ 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
|
||||||
|
if let Some(segment) = SiteMapSegment::new(&route.segments) {
|
||||||
|
let parent = site_map_stack.last_mut();
|
||||||
|
let children = match parent {
|
||||||
|
Some(parent) => &mut parent.last_mut().unwrap().children,
|
||||||
|
None => &mut site_map,
|
||||||
|
};
|
||||||
|
children.push(segment);
|
||||||
|
}
|
||||||
|
|
||||||
routes.push(route);
|
routes.push(route);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +205,7 @@ impl RouteEnum {
|
|||||||
routes,
|
routes,
|
||||||
nests,
|
nests,
|
||||||
layouts,
|
layouts,
|
||||||
|
site_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(myself)
|
Ok(myself)
|
||||||
@@ -276,6 +325,7 @@ impl RouteEnum {
|
|||||||
|
|
||||||
fn routable_impl(&self) -> TokenStream2 {
|
fn routable_impl(&self) -> TokenStream2 {
|
||||||
let name = &self.name;
|
let name = &self.name;
|
||||||
|
let site_map = &self.site_map;
|
||||||
|
|
||||||
let mut layers = Vec::new();
|
let mut layers = Vec::new();
|
||||||
|
|
||||||
@@ -304,6 +354,10 @@ impl RouteEnum {
|
|||||||
|
|
||||||
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] = &[
|
||||||
|
#(#site_map,)*
|
||||||
|
];
|
||||||
|
|
||||||
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 {
|
||||||
@@ -311,12 +365,10 @@ impl RouteEnum {
|
|||||||
#index_iter => {
|
#index_iter => {
|
||||||
match myself {
|
match myself {
|
||||||
#layers
|
#layers
|
||||||
// _ => panic!("Route::render called with invalid level {}", level),
|
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)*
|
)*
|
||||||
// _ => panic!("Route::render called with invalid level {}", level),
|
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -339,3 +391,76 @@ impl ToTokens for RouteEnum {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SiteMapSegment {
|
||||||
|
pub segment_type: SegmentType,
|
||||||
|
pub children: Vec<SiteMapSegment>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SiteMapSegment {
|
||||||
|
fn new(segments: &[RouteSegment]) -> Option<Self> {
|
||||||
|
let mut current = None;
|
||||||
|
// walk backwards through the new segments, adding children as we go
|
||||||
|
for segment in segments.iter().rev() {
|
||||||
|
let segment_type = segment.into();
|
||||||
|
let mut segment = SiteMapSegment {
|
||||||
|
segment_type,
|
||||||
|
children: Vec::new(),
|
||||||
|
};
|
||||||
|
// if we have a current segment, add it as a child
|
||||||
|
if let Some(current) = current.take() {
|
||||||
|
segment.children.push(current)
|
||||||
|
}
|
||||||
|
current = Some(segment);
|
||||||
|
}
|
||||||
|
current
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for SiteMapSegment {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||||
|
let segment_type = &self.segment_type;
|
||||||
|
let children = &self.children;
|
||||||
|
|
||||||
|
tokens.extend(quote! {
|
||||||
|
dioxus_router::routable::SiteMapSegment {
|
||||||
|
segment_type: #segment_type,
|
||||||
|
children: &[
|
||||||
|
#(#children,)*
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SegmentType {
|
||||||
|
Static(String),
|
||||||
|
Dynamic(String),
|
||||||
|
CatchAll(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for SegmentType {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||||
|
match self {
|
||||||
|
SegmentType::Static(s) => {
|
||||||
|
tokens.extend(quote! { dioxus_router::routable::SegmentType::Static(#s) })
|
||||||
|
}
|
||||||
|
SegmentType::Dynamic(s) => {
|
||||||
|
tokens.extend(quote! { dioxus_router::routable::SegmentType::Dynamic(#s) })
|
||||||
|
}
|
||||||
|
SegmentType::CatchAll(s) => {
|
||||||
|
tokens.extend(quote! { dioxus_router::routable::SegmentType::CatchAll(#s) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a RouteSegment> for SegmentType {
|
||||||
|
fn from(value: &'a RouteSegment) -> Self {
|
||||||
|
match value {
|
||||||
|
segment::RouteSegment::Static(s) => SegmentType::Static(s.to_string()),
|
||||||
|
segment::RouteSegment::Dynamic(s, _) => SegmentType::Dynamic(s.to_string()),
|
||||||
|
segment::RouteSegment::CatchAll(s, _) => SegmentType::CatchAll(s.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,6 +47,7 @@ pub mod prelude {
|
|||||||
pub use crate::components::*;
|
pub use crate::components::*;
|
||||||
pub use crate::contexts::*;
|
pub use crate::contexts::*;
|
||||||
pub use crate::hooks::*;
|
pub use crate::hooks::*;
|
||||||
|
pub use crate::routable::*;
|
||||||
pub use crate::router_cfg::RouterConfiguration;
|
pub use crate::router_cfg::RouterConfiguration;
|
||||||
pub use dioxus_router_macro::Routable;
|
pub use dioxus_router_macro::Routable;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::{fmt::Display, str::FromStr};
|
||||||
|
|
||||||
/// An error that occurs when parsing a route
|
/// An error that occurs when parsing a route
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@@ -95,6 +95,9 @@ impl<I: std::iter::FromIterator<String>> FromRouteSegments for I {
|
|||||||
|
|
||||||
/// Something that can be routed to
|
/// Something that can be routed to
|
||||||
pub trait Routable: std::fmt::Display + std::str::FromStr + Clone + 'static {
|
pub trait Routable: std::fmt::Display + std::str::FromStr + Clone + 'static {
|
||||||
|
/// The error that can occur when parsing a route
|
||||||
|
const SITE_MAP: &'static [SiteMapSegment];
|
||||||
|
|
||||||
/// Render the route at the given level
|
/// Render the route at the given level
|
||||||
fn render<'a>(&self, cx: &'a ScopeState, level: usize) -> Element<'a>;
|
fn render<'a>(&self, cx: &'a ScopeState, level: usize) -> Element<'a>;
|
||||||
}
|
}
|
||||||
@@ -124,3 +127,54 @@ where
|
|||||||
self.render(cx, level)
|
self.render(cx, level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A type erased map of the site structurens
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct SiteMapSegment {
|
||||||
|
/// The type of the route segment
|
||||||
|
pub segment_type: SegmentType,
|
||||||
|
/// The children of the route segment
|
||||||
|
pub children: &'static [SiteMapSegment],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SiteMapSegment {
|
||||||
|
/// Take a map of the site structure and flatten it into a vector of routes
|
||||||
|
pub fn flatten(&self) -> Vec<Vec<SegmentType>> {
|
||||||
|
let mut routes = Vec::new();
|
||||||
|
self.flatten_inner(&mut routes, Vec::new());
|
||||||
|
routes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flatten_inner(&self, routes: &mut Vec<Vec<SegmentType>>, current: Vec<SegmentType>) {
|
||||||
|
let mut current = current;
|
||||||
|
current.push(self.segment_type.clone());
|
||||||
|
if self.children.is_empty() {
|
||||||
|
routes.push(current);
|
||||||
|
} else {
|
||||||
|
for child in self.children {
|
||||||
|
child.flatten_inner(routes, current.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type of a route segment
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum SegmentType {
|
||||||
|
/// A static route segment
|
||||||
|
Static(&'static str),
|
||||||
|
/// A dynamic route segment
|
||||||
|
Dynamic(&'static str),
|
||||||
|
/// A catch all route segment
|
||||||
|
CatchAll(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for SegmentType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match &self {
|
||||||
|
SegmentType::Static(s) => write!(f, "/{}", s),
|
||||||
|
SegmentType::Dynamic(s) => write!(f, "/:{}", s),
|
||||||
|
SegmentType::CatchAll(s) => write!(f, "/:...{}", s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user