reorganize router macro package
This commit is contained in:
1 parent
d6b2145aa7
commit
adfc10461e
7 files changed
+183
-156
No files matched your search
@@ -1,3 +1,5 @@
|
|||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_router_core::*;
|
use dioxus_router_core::*;
|
||||||
use dioxus_router_macro::*;
|
use dioxus_router_macro::*;
|
||||||
@@ -57,6 +59,7 @@ fn Route6(cx: Scope, extra: Vec<String>) -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
#[derive(Routable, Clone, Debug, PartialEq)]
|
#[derive(Routable, Clone, Debug, PartialEq)]
|
||||||
enum Route {
|
enum Route {
|
||||||
#[route("/(dynamic)" Route1)]
|
#[route("/(dynamic)" Route1)]
|
||||||
|
|||||||
@@ -8,8 +8,11 @@ use syn::{parse_macro_input, Ident};
|
|||||||
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
|
|
||||||
|
mod nest;
|
||||||
|
mod query;
|
||||||
mod route;
|
mod route;
|
||||||
mod route_tree;
|
mod route_tree;
|
||||||
|
mod segment;
|
||||||
|
|
||||||
#[proc_macro_derive(Routable, attributes(route))]
|
#[proc_macro_derive(Routable, attributes(route))]
|
||||||
pub fn derive_routable(input: TokenStream) -> TokenStream {
|
pub fn derive_routable(input: TokenStream) -> TokenStream {
|
||||||
@@ -51,8 +54,15 @@ impl RouteEnum {
|
|||||||
if let syn::Data::Enum(data) = input.data {
|
if let syn::Data::Enum(data) = input.data {
|
||||||
let mut routes = Vec::new();
|
let mut routes = Vec::new();
|
||||||
|
|
||||||
|
let mut current_base_route = String::new();
|
||||||
|
|
||||||
for variant in data.variants {
|
for variant in data.variants {
|
||||||
let route = Route::parse(variant)?;
|
// Apply the any nesting attributes in order
|
||||||
|
for attr in &variant.attrs {
|
||||||
|
if attr.path.is_ident("nest") {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let route = Route::parse(current_base_route.clone(), variant)?;
|
||||||
routes.push(route);
|
routes.push(route);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
struct Nest {}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
use quote::quote;
|
||||||
|
use syn::{Ident, Type};
|
||||||
|
|
||||||
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct QuerySegment {
|
||||||
|
pub ident: Ident,
|
||||||
|
pub ty: Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QuerySegment {
|
||||||
|
pub fn parse(&self) -> TokenStream2 {
|
||||||
|
let ident = &self.ident;
|
||||||
|
let ty = &self.ty;
|
||||||
|
quote! {
|
||||||
|
let #ident = <#ty as dioxus_router_core::router::FromQuery>::from_query(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self) -> TokenStream2 {
|
||||||
|
let ident = &self.ident;
|
||||||
|
quote! {
|
||||||
|
write!(f, "?{}", #ident)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> Ident {
|
||||||
|
self.ident.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
use quote::{__private::Span, format_ident, quote, ToTokens};
|
use quote::{__private::Span, format_ident, quote, ToTokens};
|
||||||
use syn::parse::Parse;
|
use syn::parse::Parse;
|
||||||
use syn::parse::ParseStream;
|
use syn::parse::ParseStream;
|
||||||
use syn::{Ident, LitStr, Type, Variant};
|
use syn::{Ident, LitStr, Variant};
|
||||||
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
|
|
||||||
|
use crate::query::QuerySegment;
|
||||||
|
use crate::segment::RouteSegment;
|
||||||
|
|
||||||
struct RouteArgs {
|
struct RouteArgs {
|
||||||
route: LitStr,
|
route: LitStr,
|
||||||
comp_name: Option<Ident>,
|
comp_name: Option<Ident>,
|
||||||
@@ -35,13 +38,13 @@ pub struct Route {
|
|||||||
pub route_name: Ident,
|
pub route_name: Ident,
|
||||||
pub comp_name: Ident,
|
pub comp_name: Ident,
|
||||||
pub props_name: Ident,
|
pub props_name: Ident,
|
||||||
pub route: LitStr,
|
pub route: String,
|
||||||
pub route_segments: Vec<RouteSegment>,
|
pub route_segments: Vec<RouteSegment>,
|
||||||
pub query: Option<QuerySegment>,
|
pub query: Option<QuerySegment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Route {
|
impl Route {
|
||||||
pub fn parse(input: syn::Variant) -> syn::Result<Self> {
|
pub fn parse(root_route: String, input: syn::Variant) -> syn::Result<Self> {
|
||||||
let route_attr = input
|
let route_attr = input
|
||||||
.attrs
|
.attrs
|
||||||
.iter()
|
.iter()
|
||||||
@@ -55,7 +58,7 @@ impl Route {
|
|||||||
|
|
||||||
let route_name = input.ident.clone();
|
let route_name = input.ident.clone();
|
||||||
let args = route_attr.parse_args::<RouteArgs>()?;
|
let args = route_attr.parse_args::<RouteArgs>()?;
|
||||||
let route = args.route;
|
let route = root_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
|
||||||
@@ -209,7 +212,7 @@ impl ToTokens for Route {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let without_leading_slash = &self.route.value()[1..];
|
let without_leading_slash = &self.route[1..];
|
||||||
let route_path = std::path::Path::new(without_leading_slash);
|
let route_path = std::path::Path::new(without_leading_slash);
|
||||||
let with_extension = route_path.with_extension("rs");
|
let with_extension = route_path.with_extension("rs");
|
||||||
let dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
let dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||||
@@ -237,14 +240,13 @@ impl ToTokens for Route {
|
|||||||
|
|
||||||
fn parse_route_segments(
|
fn parse_route_segments(
|
||||||
varient: &Variant,
|
varient: &Variant,
|
||||||
route: &LitStr,
|
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();
|
||||||
|
|
||||||
let route_string = route.value();
|
let (route_string, query) = match route.rsplit_once('?') {
|
||||||
let (route_string, query) = match route_string.rsplit_once('?') {
|
|
||||||
Some((route, query)) => (route, Some(query)),
|
Some((route, query)) => (route, Some(query)),
|
||||||
None => (route_string.as_str(), None),
|
None => (route, None),
|
||||||
};
|
};
|
||||||
let mut iterator = route_string.split('/');
|
let mut iterator = route_string.split('/');
|
||||||
|
|
||||||
@@ -255,7 +257,7 @@ fn parse_route_segments(
|
|||||||
varient,
|
varient,
|
||||||
format!(
|
format!(
|
||||||
"Routes should start with /. Error found in the route '{}'",
|
"Routes should start with /. Error found in the route '{}'",
|
||||||
route.value()
|
route
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -346,147 +348,3 @@ fn parse_route_segments(
|
|||||||
|
|
||||||
Ok((route_segments, parsed_query))
|
Ok((route_segments, parsed_query))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum RouteSegment {
|
|
||||||
Static(String),
|
|
||||||
Dynamic(Ident, Type),
|
|
||||||
CatchAll(Ident, Type),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RouteSegment {
|
|
||||||
pub fn name(&self) -> Option<Ident> {
|
|
||||||
match self {
|
|
||||||
Self::Static(_) => None,
|
|
||||||
Self::Dynamic(ident, _) => Some(ident.clone()),
|
|
||||||
Self::CatchAll(ident, _) => Some(ident.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_segment(&self) -> TokenStream2 {
|
|
||||||
match self {
|
|
||||||
Self::Static(segment) => quote! { write!(f, "/{}", #segment)?; },
|
|
||||||
Self::Dynamic(ident, _) => quote! { write!(f, "/{}", #ident)?; },
|
|
||||||
Self::CatchAll(ident, _) => quote! { #ident.display_route_segements(f)?; },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn error_name(&self, idx: usize) -> Ident {
|
|
||||||
match self {
|
|
||||||
Self::Static(_) => static_segment_idx(idx),
|
|
||||||
Self::Dynamic(ident, _) => format_ident!("{}ParseError", ident),
|
|
||||||
Self::CatchAll(ident, _) => format_ident!("{}ParseError", ident),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn missing_error_name(&self) -> Option<Ident> {
|
|
||||||
match self {
|
|
||||||
Self::Dynamic(ident, _) => Some(format_ident!("{}MissingError", ident)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_parse(
|
|
||||||
&self,
|
|
||||||
idx: usize,
|
|
||||||
error_enum_name: &Ident,
|
|
||||||
error_enum_varient: &Ident,
|
|
||||||
inner_parse_enum: &Ident,
|
|
||||||
parse_children: TokenStream2,
|
|
||||||
) -> TokenStream2 {
|
|
||||||
let error_name = self.error_name(idx);
|
|
||||||
match self {
|
|
||||||
Self::Static(segment) => {
|
|
||||||
quote! {
|
|
||||||
{
|
|
||||||
let mut segments = segments.clone();
|
|
||||||
let parsed = if let Some(#segment) = segments.next() {
|
|
||||||
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(name, ty) => {
|
|
||||||
let missing_error_name = self.missing_error_name().unwrap();
|
|
||||||
quote! {
|
|
||||||
{
|
|
||||||
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(name, ty) => {
|
|
||||||
quote! {
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn static_segment_idx(idx: usize) -> Ident {
|
|
||||||
format_ident!("StaticSegment{}ParseError", idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct QuerySegment {
|
|
||||||
ident: Ident,
|
|
||||||
ty: Type,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl QuerySegment {
|
|
||||||
pub fn parse(&self) -> TokenStream2 {
|
|
||||||
let ident = &self.ident;
|
|
||||||
let ty = &self.ty;
|
|
||||||
quote! {
|
|
||||||
let #ident = <#ty as dioxus_router_core::router::FromQuery>::from_query(query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(&self) -> TokenStream2 {
|
|
||||||
let ident = &self.ident;
|
|
||||||
quote! {
|
|
||||||
write!(f, "?{}", #ident)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn name(&self) -> Ident {
|
|
||||||
self.ident.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,10 @@ use proc_macro2::TokenStream;
|
|||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::Ident;
|
use syn::Ident;
|
||||||
|
|
||||||
use crate::route::{static_segment_idx, Route, RouteSegment};
|
use crate::{
|
||||||
|
route::Route,
|
||||||
|
segment::{static_segment_idx, RouteSegment},
|
||||||
|
};
|
||||||
|
|
||||||
// First deduplicate the routes by the static part of the route
|
// First deduplicate the routes by the static part of the route
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
use quote::{format_ident, quote};
|
||||||
|
use syn::{Ident, Type};
|
||||||
|
|
||||||
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RouteSegment {
|
||||||
|
Static(String),
|
||||||
|
Dynamic(Ident, Type),
|
||||||
|
CatchAll(Ident, Type),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RouteSegment {
|
||||||
|
pub fn name(&self) -> Option<Ident> {
|
||||||
|
match self {
|
||||||
|
Self::Static(_) => None,
|
||||||
|
Self::Dynamic(ident, _) => Some(ident.clone()),
|
||||||
|
Self::CatchAll(ident, _) => Some(ident.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_segment(&self) -> TokenStream2 {
|
||||||
|
match self {
|
||||||
|
Self::Static(segment) => quote! { write!(f, "/{}", #segment)?; },
|
||||||
|
Self::Dynamic(ident, _) => quote! { write!(f, "/{}", #ident)?; },
|
||||||
|
Self::CatchAll(ident, _) => quote! { #ident.display_route_segements(f)?; },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error_name(&self, idx: usize) -> Ident {
|
||||||
|
match self {
|
||||||
|
Self::Static(_) => static_segment_idx(idx),
|
||||||
|
Self::Dynamic(ident, _) => format_ident!("{}ParseError", ident),
|
||||||
|
Self::CatchAll(ident, _) => format_ident!("{}ParseError", ident),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn missing_error_name(&self) -> Option<Ident> {
|
||||||
|
match self {
|
||||||
|
Self::Dynamic(ident, _) => Some(format_ident!("{}MissingError", ident)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_parse(
|
||||||
|
&self,
|
||||||
|
idx: usize,
|
||||||
|
error_enum_name: &Ident,
|
||||||
|
error_enum_varient: &Ident,
|
||||||
|
inner_parse_enum: &Ident,
|
||||||
|
parse_children: TokenStream2,
|
||||||
|
) -> TokenStream2 {
|
||||||
|
let error_name = self.error_name(idx);
|
||||||
|
match self {
|
||||||
|
Self::Static(segment) => {
|
||||||
|
quote! {
|
||||||
|
{
|
||||||
|
let mut segments = segments.clone();
|
||||||
|
let parsed = if let Some(#segment) = segments.next() {
|
||||||
|
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(name, ty) => {
|
||||||
|
let missing_error_name = self.missing_error_name().unwrap();
|
||||||
|
quote! {
|
||||||
|
{
|
||||||
|
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(name, ty) => {
|
||||||
|
quote! {
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn static_segment_idx(idx: usize) -> Ident {
|
||||||
|
format_ident!("StaticSegment{}ParseError", idx)
|
||||||
|
}
|
||||||
Reference in new issue
Block a user