mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 14:54:16 +00:00
abstract interface to walk nested routes and to access views
This commit is contained in:
parent
ca54762806
commit
b46dffb729
3 changed files with 140 additions and 69 deletions
|
@ -36,7 +36,7 @@ impl<'a, Children> Routes<Children>
|
|||
where
|
||||
Children: MatchNestedRoutes<'a>,
|
||||
{
|
||||
pub fn match_route(&self, path: &'a str) -> Option<Children::Match> {
|
||||
pub fn match_route(&'a self, path: &'a str) -> Option<Children::Match> {
|
||||
let path = match &self.base {
|
||||
None => path,
|
||||
Some(base) => {
|
||||
|
@ -72,18 +72,23 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub trait IntoParams<'a> {
|
||||
type IntoParams: IntoIterator<Item = (&'a str, &'a str)>;
|
||||
pub trait MatchInterface<'a> {
|
||||
type Params: IntoIterator<Item = (&'a str, &'a str)>;
|
||||
type Child;
|
||||
type View;
|
||||
|
||||
fn to_params(&self) -> Self::IntoParams;
|
||||
fn to_params(&self) -> Self::Params;
|
||||
|
||||
fn to_child(&'a self) -> Self::Child;
|
||||
|
||||
fn to_view(&self) -> Self::View;
|
||||
}
|
||||
|
||||
pub trait MatchNestedRoutes<'a> {
|
||||
type Data;
|
||||
//type ParamsIter: IntoIterator<Item = (&'a str, &'a str)> + Clone;
|
||||
type Match: IntoParams<'a>;
|
||||
type Match: MatchInterface<'a>;
|
||||
|
||||
fn match_nested(&self, path: &'a str) -> (Option<Self::Match>, &'a str);
|
||||
fn match_nested(&'a self, path: &'a str) -> (Option<Self::Match>, &'a str);
|
||||
|
||||
fn generate_routes(
|
||||
&self,
|
||||
|
@ -94,7 +99,7 @@ pub trait MatchNestedRoutes<'a> {
|
|||
mod tests {
|
||||
use super::{NestedRoute, ParamSegment, Routes};
|
||||
use crate::{
|
||||
matching::{IntoParams, StaticSegment, WildcardSegment},
|
||||
matching::{MatchInterface, StaticSegment, WildcardSegment},
|
||||
PathSegment,
|
||||
};
|
||||
|
||||
|
@ -104,7 +109,7 @@ mod tests {
|
|||
segments: StaticSegment("/"),
|
||||
children: (),
|
||||
data: (),
|
||||
view: (),
|
||||
view: || (),
|
||||
});
|
||||
let matched = routes.match_route("/");
|
||||
assert!(matched.is_some());
|
||||
|
@ -129,9 +134,8 @@ mod tests {
|
|||
data: (),
|
||||
view: "Home",
|
||||
});
|
||||
let matched = routes.match_route("/author/contact").unwrap();
|
||||
assert_eq!(matched.matched(), "");
|
||||
assert_eq!(matched.child().matched(), "/author/contact");
|
||||
|
||||
// route generation
|
||||
let (base, paths) = routes.generate_routes();
|
||||
assert_eq!(base, None);
|
||||
let paths = paths.into_iter().collect::<Vec<_>>();
|
||||
|
@ -143,6 +147,14 @@ mod tests {
|
|||
PathSegment::Static("contact".into())
|
||||
]]
|
||||
);
|
||||
|
||||
let matched = routes.match_route("/author/contact").unwrap();
|
||||
assert_eq!(matched.matched(), "");
|
||||
assert_eq!(matched.to_child().matched(), "/author/contact");
|
||||
|
||||
let view = matched.to_view();
|
||||
assert_eq!(*view, "Home");
|
||||
assert_eq!(*matched.to_child().to_view(), "Contact Me");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -172,17 +184,17 @@ mod tests {
|
|||
segments: StaticSegment(""),
|
||||
children: (),
|
||||
data: (),
|
||||
view: (),
|
||||
view: || (),
|
||||
},
|
||||
NestedRoute {
|
||||
segments: StaticSegment("about"),
|
||||
children: (),
|
||||
data: (),
|
||||
view: (),
|
||||
view: || (),
|
||||
},
|
||||
),
|
||||
data: (),
|
||||
view: (),
|
||||
view: || (),
|
||||
},
|
||||
NestedRoute {
|
||||
segments: StaticSegment("/blog"),
|
||||
|
@ -191,17 +203,17 @@ mod tests {
|
|||
segments: StaticSegment(""),
|
||||
children: (),
|
||||
data: (),
|
||||
view: (),
|
||||
view: || (),
|
||||
},
|
||||
NestedRoute {
|
||||
segments: (StaticSegment("post"), ParamSegment("id")),
|
||||
children: (),
|
||||
data: (),
|
||||
view: (),
|
||||
view: || (),
|
||||
},
|
||||
),
|
||||
data: (),
|
||||
view: (),
|
||||
view: || (),
|
||||
},
|
||||
));
|
||||
|
||||
|
@ -254,17 +266,17 @@ mod tests {
|
|||
segments: StaticSegment("/"),
|
||||
children: (),
|
||||
data: (),
|
||||
view: (),
|
||||
view: || (),
|
||||
},
|
||||
NestedRoute {
|
||||
segments: StaticSegment("about"),
|
||||
children: (),
|
||||
data: (),
|
||||
view: (),
|
||||
view: || (),
|
||||
},
|
||||
),
|
||||
data: (),
|
||||
view: (),
|
||||
view: || (),
|
||||
},
|
||||
NestedRoute {
|
||||
segments: StaticSegment("/blog"),
|
||||
|
@ -273,13 +285,13 @@ mod tests {
|
|||
segments: StaticSegment(""),
|
||||
children: (),
|
||||
data: (),
|
||||
view: (),
|
||||
view: || (),
|
||||
},
|
||||
NestedRoute {
|
||||
segments: StaticSegment("category"),
|
||||
children: (),
|
||||
data: (),
|
||||
view: (),
|
||||
view: || (),
|
||||
},
|
||||
NestedRoute {
|
||||
segments: (
|
||||
|
@ -288,11 +300,11 @@ mod tests {
|
|||
),
|
||||
children: (),
|
||||
data: (),
|
||||
view: (),
|
||||
view: || (),
|
||||
},
|
||||
),
|
||||
data: (),
|
||||
view: (),
|
||||
view: || (),
|
||||
},
|
||||
NestedRoute {
|
||||
segments: (
|
||||
|
@ -301,7 +313,7 @@ mod tests {
|
|||
),
|
||||
children: (),
|
||||
data: (),
|
||||
view: (),
|
||||
view: || (),
|
||||
},
|
||||
),
|
||||
"/portfolio",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::{
|
||||
IntoParams, MatchNestedRoutes, PartialPathMatch, PossibleRouteMatch,
|
||||
MatchInterface, MatchNestedRoutes, PartialPathMatch, PossibleRouteMatch,
|
||||
};
|
||||
use crate::PathSegment;
|
||||
use core::iter;
|
||||
|
@ -7,61 +7,71 @@ use core::iter;
|
|||
mod tuples;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct NestedRoute<Segments, Children, Data, ViewFn> {
|
||||
pub struct NestedRoute<Segments, Children, Data, View> {
|
||||
pub segments: Segments,
|
||||
pub children: Children,
|
||||
pub data: Data,
|
||||
pub view: ViewFn,
|
||||
pub view: View,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct NestedMatch<'a, ParamsIter, Child> {
|
||||
pub struct NestedMatch<'a, ParamsIter, Child, View> {
|
||||
/// The portion of the full path matched only by this nested route.
|
||||
matched: &'a str,
|
||||
/// The map of params matched only by this nested route.
|
||||
params: ParamsIter,
|
||||
/// The nested route.
|
||||
child: Child,
|
||||
view: &'a View,
|
||||
}
|
||||
|
||||
impl<'a, ParamsIter, Child> IntoParams<'a>
|
||||
for NestedMatch<'a, ParamsIter, Child>
|
||||
impl<'a, ParamsIter, Child, View> MatchInterface<'a>
|
||||
for NestedMatch<'a, ParamsIter, Child, View>
|
||||
where
|
||||
ParamsIter: IntoIterator<Item = (&'a str, &'a str)> + Clone,
|
||||
Child: 'a,
|
||||
{
|
||||
type IntoParams = ParamsIter;
|
||||
type Params = ParamsIter;
|
||||
type Child = &'a Child;
|
||||
type View = &'a View;
|
||||
|
||||
fn to_params(&self) -> Self::IntoParams {
|
||||
fn to_params(&self) -> Self::Params {
|
||||
self.params.clone()
|
||||
}
|
||||
|
||||
fn to_child(&'a self) -> Self::Child {
|
||||
&self.child
|
||||
}
|
||||
|
||||
fn to_view(&self) -> Self::View {
|
||||
self.view
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, ParamsIter, Child> NestedMatch<'a, ParamsIter, Child> {
|
||||
impl<'a, ParamsIter, Child, View> NestedMatch<'a, ParamsIter, Child, View> {
|
||||
pub fn matched(&self) -> &'a str {
|
||||
self.matched
|
||||
}
|
||||
|
||||
pub fn child(&self) -> &Child {
|
||||
&self.child
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Segments, Children, Data, ViewFn> MatchNestedRoutes<'a>
|
||||
for NestedRoute<Segments, Children, Data, ViewFn>
|
||||
impl<'a, Segments, Children, Data, View> MatchNestedRoutes<'a>
|
||||
for NestedRoute<Segments, Children, Data, View>
|
||||
where
|
||||
Segments: PossibleRouteMatch,
|
||||
Children: MatchNestedRoutes<'a>,
|
||||
<Segments::ParamsIter<'a> as IntoIterator>::IntoIter: Clone,
|
||||
<<Children::Match as IntoParams<'a>>::IntoParams as IntoIterator>::IntoIter:
|
||||
<<Children::Match as MatchInterface<'a>>::Params as IntoIterator>::IntoIter:
|
||||
Clone,
|
||||
Children: 'a,
|
||||
View: 'a,
|
||||
{
|
||||
type Data = Data;
|
||||
type Match = NestedMatch<'a, iter::Chain<
|
||||
<Segments::ParamsIter<'a> as IntoIterator>::IntoIter,
|
||||
<<Children::Match as IntoParams<'a>>::IntoParams as IntoIterator>::IntoIter,
|
||||
>, Children::Match>;
|
||||
<<Children::Match as MatchInterface<'a>>::Params as IntoIterator>::IntoIter,
|
||||
>, Children::Match, View>;
|
||||
|
||||
fn match_nested(&self, path: &'a str) -> (Option<Self::Match>, &'a str) {
|
||||
fn match_nested(&'a self, path: &'a str) -> (Option<Self::Match>, &'a str) {
|
||||
self.segments
|
||||
.test(path)
|
||||
.and_then(
|
||||
|
@ -81,6 +91,7 @@ where
|
|||
matched,
|
||||
params: params.chain(inner.to_params()),
|
||||
child: inner,
|
||||
view: &self.view,
|
||||
}),
|
||||
remaining,
|
||||
))
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
use crate::{
|
||||
matching::{IntoParams, MatchNestedRoutes},
|
||||
matching::{MatchInterface, MatchNestedRoutes},
|
||||
PathSegment,
|
||||
};
|
||||
use core::iter;
|
||||
use either_of::*;
|
||||
|
||||
impl<'a> IntoParams<'a> for () {
|
||||
type IntoParams = iter::Empty<(&'a str, &'a str)>;
|
||||
impl<'a> MatchInterface<'a> for () {
|
||||
type Params = iter::Empty<(&'a str, &'a str)>;
|
||||
type Child = ();
|
||||
type View = ();
|
||||
|
||||
fn to_params(&self) -> Self::IntoParams {
|
||||
fn to_params(&self) -> Self::Params {
|
||||
iter::empty()
|
||||
}
|
||||
|
||||
fn to_child(&self) -> Self::Child {}
|
||||
|
||||
fn to_view(&self) -> Self::View {}
|
||||
}
|
||||
|
||||
impl<'a> MatchNestedRoutes<'a> for () {
|
||||
type Data = ();
|
||||
//type ParamsIter = iter::Empty<(&'a str, &'a str)>;
|
||||
type Match = ();
|
||||
|
||||
fn match_nested(&self, path: &'a str) -> (Option<Self::Match>, &'a str) {
|
||||
|
@ -29,15 +34,25 @@ impl<'a> MatchNestedRoutes<'a> for () {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, A> IntoParams<'a> for (A,)
|
||||
impl<'a, A> MatchInterface<'a> for (A,)
|
||||
where
|
||||
A: IntoParams<'a>,
|
||||
A: MatchInterface<'a>,
|
||||
{
|
||||
type IntoParams = A::IntoParams;
|
||||
type Params = A::Params;
|
||||
type Child = A::Child;
|
||||
type View = A::View;
|
||||
|
||||
fn to_params(&self) -> Self::IntoParams {
|
||||
fn to_params(&self) -> Self::Params {
|
||||
self.0.to_params()
|
||||
}
|
||||
|
||||
fn to_child(&'a self) -> Self::Child {
|
||||
self.0.to_child()
|
||||
}
|
||||
|
||||
fn to_view(&self) -> Self::View {
|
||||
self.0.to_view()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A> MatchNestedRoutes<'a> for (A,)
|
||||
|
@ -47,7 +62,7 @@ where
|
|||
type Data = A::Data;
|
||||
type Match = A::Match;
|
||||
|
||||
fn match_nested(&self, path: &'a str) -> (Option<Self::Match>, &'a str) {
|
||||
fn match_nested(&'a self, path: &'a str) -> (Option<Self::Match>, &'a str) {
|
||||
self.0.match_nested(path)
|
||||
}
|
||||
|
||||
|
@ -58,22 +73,38 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, A, B> IntoParams<'a> for Either<A, B>
|
||||
impl<'a, A, B> MatchInterface<'a> for Either<A, B>
|
||||
where
|
||||
A: IntoParams<'a>,
|
||||
B: IntoParams<'a>,
|
||||
A: MatchInterface<'a>,
|
||||
B: MatchInterface<'a>,
|
||||
{
|
||||
type IntoParams = Either<
|
||||
<A::IntoParams as IntoIterator>::IntoIter,
|
||||
<B::IntoParams as IntoIterator>::IntoIter,
|
||||
type Params = Either<
|
||||
<A::Params as IntoIterator>::IntoIter,
|
||||
<B::Params as IntoIterator>::IntoIter,
|
||||
>;
|
||||
type Child = Either<A::Child, B::Child>;
|
||||
type View = Either<A::View, B::View>;
|
||||
|
||||
fn to_params(&self) -> Self::IntoParams {
|
||||
fn to_params(&self) -> Self::Params {
|
||||
match self {
|
||||
Either::Left(i) => Either::Left(i.to_params().into_iter()),
|
||||
Either::Right(i) => Either::Right(i.to_params().into_iter()),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_child(&'a self) -> Self::Child {
|
||||
match self {
|
||||
Either::Left(i) => Either::Left(i.to_child()),
|
||||
Either::Right(i) => Either::Right(i.to_child()),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_view(&self) -> Self::View {
|
||||
match self {
|
||||
Either::Left(i) => Either::Left(i.to_view()),
|
||||
Either::Right(i) => Either::Right(i.to_view()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A, B> MatchNestedRoutes<'a> for (A, B)
|
||||
|
@ -84,7 +115,7 @@ where
|
|||
type Data = (A::Data, B::Data);
|
||||
type Match = Either<A::Match, B::Match>;
|
||||
|
||||
fn match_nested(&self, path: &'a str) -> (Option<Self::Match>, &'a str) {
|
||||
fn match_nested(&'a self, path: &'a str) -> (Option<Self::Match>, &'a str) {
|
||||
#[allow(non_snake_case)]
|
||||
let (A, B) = &self;
|
||||
if let (Some(matched), remaining) = A.match_nested(path) {
|
||||
|
@ -124,29 +155,46 @@ macro_rules! chain_generated {
|
|||
|
||||
macro_rules! tuples {
|
||||
($either:ident => $($ty:ident),*) => {
|
||||
impl<'a, $($ty,)*> IntoParams<'a> for $either <$($ty,)*>
|
||||
impl<'a, $($ty,)*> MatchInterface<'a> for $either <$($ty,)*>
|
||||
where
|
||||
$($ty: IntoParams<'a>),*,
|
||||
$($ty: MatchInterface<'a>),*,
|
||||
$($ty::Child: 'a),*,
|
||||
$($ty::View: 'a),*,
|
||||
{
|
||||
type IntoParams = $either<$(
|
||||
<$ty::IntoParams as IntoIterator>::IntoIter,
|
||||
type Params = $either<$(
|
||||
<$ty::Params as IntoIterator>::IntoIter,
|
||||
)*>;
|
||||
type Child = $either<$($ty::Child,)*>;
|
||||
type View = $either<$($ty::View,)*>;
|
||||
|
||||
fn to_params(&self) -> Self::IntoParams {
|
||||
fn to_params(&self) -> Self::Params {
|
||||
match self {
|
||||
$($either::$ty(i) => $either::$ty(i.to_params().into_iter()),)*
|
||||
}
|
||||
}
|
||||
|
||||
fn to_child(&'a self) -> Self::Child {
|
||||
match self {
|
||||
$($either::$ty(i) => $either::$ty(i.to_child()),)*
|
||||
}
|
||||
}
|
||||
|
||||
fn to_view(&self) -> Self::View {
|
||||
match self {
|
||||
$($either::$ty(i) => $either::$ty(i.to_view()),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, $($ty),*> MatchNestedRoutes<'a> for ($($ty,)*)
|
||||
where
|
||||
$($ty: MatchNestedRoutes<'a>),*,
|
||||
$($ty::Match: 'a),*,
|
||||
{
|
||||
type Data = ($($ty::Data,)*);
|
||||
type Match = $either<$($ty::Match,)*>;
|
||||
|
||||
fn match_nested(&self, path: &'a str) -> (Option<Self::Match>, &'a str) {
|
||||
fn match_nested(&'a self, path: &'a str) -> (Option<Self::Match>, &'a str) {
|
||||
#[allow(non_snake_case)]
|
||||
|
||||
let ($($ty,)*) = &self;
|
||||
|
|
Loading…
Reference in a new issue