mirror of
https://github.com/leptos-rs/leptos
synced 2024-09-20 14:32:00 +00:00
get basic routing working
This commit is contained in:
parent
9f02cc8cc1
commit
84ebdc1b92
9 changed files with 408 additions and 85 deletions
|
@ -12,7 +12,7 @@ use leptos::{
|
|||
use log::{debug, info};
|
||||
use routing::{
|
||||
location::{BrowserUrl, Location},
|
||||
NestedRoute, Router, Routes, StaticSegment,
|
||||
NestedRoute, ParamSegment, Router, Routes, StaticSegment,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
|
@ -28,6 +28,25 @@ pub fn RouterExample() -> impl IntoView {
|
|||
let router = Router::new(
|
||||
BrowserUrl::new().unwrap(),
|
||||
Routes::new((
|
||||
NestedRoute {
|
||||
segments: StaticSegment(""),
|
||||
children: (
|
||||
NestedRoute {
|
||||
segments: ParamSegment("id"),
|
||||
children: (),
|
||||
data: (),
|
||||
view: Contact,
|
||||
},
|
||||
NestedRoute {
|
||||
segments: StaticSegment(""),
|
||||
children: (),
|
||||
data: (),
|
||||
view: || "Select a contact.",
|
||||
},
|
||||
),
|
||||
data: (),
|
||||
view: ContactList,
|
||||
},
|
||||
NestedRoute {
|
||||
segments: StaticSegment("settings"),
|
||||
children: (),
|
||||
|
@ -122,19 +141,28 @@ pub fn ContactRoutes() -> impl IntoView {
|
|||
</Route>
|
||||
}
|
||||
}*/
|
||||
/*
|
||||
|
||||
#[component]
|
||||
pub fn ContactList() -> impl IntoView {
|
||||
log::debug!("rendering <ContactList/>");
|
||||
info!("rendering <ContactList/>");
|
||||
|
||||
// contexts are passed down through the route tree
|
||||
provide_context(ExampleContext(42));
|
||||
|
||||
on_cleanup(|| {
|
||||
Owner::on_cleanup(|| {
|
||||
info!("cleaning up <ContactList/>");
|
||||
});
|
||||
|
||||
let location = use_location();
|
||||
view! {
|
||||
<div class="contact-list">
|
||||
<h1>"Contacts"</h1>
|
||||
<li><a href="/1">1</a></li>
|
||||
<li><a href="/2">2</a></li>
|
||||
<li><a href="/3">3</a></li>
|
||||
</div>
|
||||
}
|
||||
|
||||
/*let location = use_location();
|
||||
let contacts = create_resource(move || location.search.get(), get_contacts);
|
||||
let contacts = move || {
|
||||
contacts.get().map(|contacts| {
|
||||
|
@ -162,14 +190,14 @@ pub fn ContactList() -> impl IntoView {
|
|||
intro="fadeIn"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}*/
|
||||
}*/
|
||||
}
|
||||
/*
|
||||
#[derive(Params, PartialEq, Clone, Debug)]
|
||||
pub struct ContactParams {
|
||||
// Params isn't implemented for usize, only Option<usize>
|
||||
id: Option<usize>,
|
||||
}
|
||||
}*/
|
||||
|
||||
#[component]
|
||||
pub fn Contact() -> impl IntoView {
|
||||
|
@ -180,11 +208,17 @@ pub fn Contact() -> impl IntoView {
|
|||
use_context::<ExampleContext>()
|
||||
);
|
||||
|
||||
on_cleanup(|| {
|
||||
Owner::on_cleanup(|| {
|
||||
info!("cleaning up <Contact/>");
|
||||
});
|
||||
|
||||
let params = use_params::<ContactParams>();
|
||||
view! {
|
||||
<div class="contact">
|
||||
<h2>"Contact"</h2>
|
||||
</div>
|
||||
}
|
||||
|
||||
/* let params = use_params::<ContactParams>();
|
||||
let contact = create_resource(
|
||||
move || {
|
||||
params
|
||||
|
@ -229,8 +263,8 @@ pub fn Contact() -> impl IntoView {
|
|||
{contact_display}
|
||||
</Transition>
|
||||
</div>
|
||||
}
|
||||
}*/
|
||||
}*/
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn About() -> impl IntoView {
|
||||
|
@ -271,7 +305,6 @@ pub fn Settings() -> impl IntoView {
|
|||
});
|
||||
|
||||
view! {
|
||||
<>
|
||||
<h1>"Settings"</h1>
|
||||
<form>
|
||||
<fieldset>
|
||||
|
@ -281,6 +314,5 @@ pub fn Settings() -> impl IntoView {
|
|||
</fieldset>
|
||||
<pre>"This page is just a placeholder."</pre>
|
||||
</form>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,9 @@ use tracing_subscriber_wasm::MakeConsoleWriter;
|
|||
pub fn main() {
|
||||
fmt()
|
||||
.with_writer(
|
||||
// To avoide trace events in the browser from showing their
|
||||
// JS backtrace, which is very annoying, in my opinion
|
||||
MakeConsoleWriter::default()
|
||||
.map_trace_level_to(tracing::Level::DEBUG),
|
||||
)
|
||||
// For some reason, if we don't do this in the browser, we get
|
||||
// a runtime error.
|
||||
.without_time()
|
||||
.init();
|
||||
console_error_panic_hook::set_once();
|
||||
|
|
|
@ -38,6 +38,18 @@ impl Owner {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn child(&self) -> Self {
|
||||
let parent = Some(Arc::downgrade(&self.inner));
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(OwnerInner {
|
||||
parent,
|
||||
nodes: Default::default(),
|
||||
contexts: Default::default(),
|
||||
cleanups: Default::default(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with<T>(&self, fun: impl FnOnce() -> T) -> T {
|
||||
let prev = {
|
||||
OWNER.with(|o| {
|
||||
|
|
|
@ -13,6 +13,7 @@ url = "2"
|
|||
js-sys = { version = "0.3" }
|
||||
wasm-bindgen = { version = "0.2" }
|
||||
tracing = { version = "0.1", optional = true }
|
||||
paste = "1.0.14"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
|
@ -41,3 +42,6 @@ features = [
|
|||
"RequestMode",
|
||||
"Response",
|
||||
]
|
||||
|
||||
[features]
|
||||
tracing = ["dep:tracing", "routing_utils/tracing"]
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
use crate::{generate_route_list::RouteList, location::Location};
|
||||
use crate::{generate_route_list::RouteList, location::Location, Params};
|
||||
use core::marker::PhantomData;
|
||||
use either_of::Either;
|
||||
use either_of::*;
|
||||
use reactive_graph::{
|
||||
computed::ArcMemo,
|
||||
effect::RenderEffect,
|
||||
owner::Owner,
|
||||
traits::{Read, Track},
|
||||
};
|
||||
use routing_utils::{
|
||||
MatchInterface, MatchNestedRoutes, PossibleRouteMatch, Routes,
|
||||
MatchInterface, MatchNestedRoutes, PossibleRouteMatch, RouteMatchId, Routes,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use tachys::{
|
||||
|
@ -16,8 +17,8 @@ use tachys::{
|
|||
renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
view::{
|
||||
add_attr::AddAnyAttr, either::EitherState, Position, PositionState,
|
||||
Render, RenderHtml,
|
||||
add_attr::AddAnyAttr, either::EitherState, Mountable, Position,
|
||||
PositionState, Render, RenderHtml,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -125,7 +126,11 @@ where
|
|||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type State = RenderEffect<
|
||||
EitherState<View::State, <Fallback as Render<Rndr>>::State, Rndr>,
|
||||
EitherState<
|
||||
NestedRouteState<View::State>,
|
||||
<Fallback as Render<Rndr>>::State,
|
||||
Rndr,
|
||||
>,
|
||||
>;
|
||||
type FallibleState = ();
|
||||
|
||||
|
@ -136,27 +141,47 @@ where
|
|||
let url = url.clone();
|
||||
move |_| url.read().path().to_string()
|
||||
});
|
||||
let search_parans = ArcMemo::new({
|
||||
let search_params = ArcMemo::new({
|
||||
let url = url.clone();
|
||||
move |_| url.read().search_params().clone()
|
||||
});
|
||||
RenderEffect::new(move |prev| {
|
||||
tachys::dom::log(&format!("recalculating route"));
|
||||
let outer_owner =
|
||||
Owner::current().expect("creating Router, but no Owner was found");
|
||||
|
||||
RenderEffect::new(move |prev: Option<EitherState<_, _, _>>| {
|
||||
let path = path.read();
|
||||
let new_view = match self.routes.match_route(&*path) {
|
||||
Some(matched) => {
|
||||
let view = matched.to_view();
|
||||
let view = view.choose();
|
||||
Either::Left(view)
|
||||
}
|
||||
_ => Either::Right((self.fallback)()),
|
||||
};
|
||||
let new_match = self.routes.match_route(&path);
|
||||
|
||||
if let Some(mut prev) = prev {
|
||||
new_view.rebuild(&mut prev);
|
||||
if let Some(new_match) = new_match {
|
||||
match &mut prev.state {
|
||||
Either::Left(prev) => {
|
||||
nested_rebuild(&outer_owner, prev, new_match);
|
||||
}
|
||||
Either::Right(_) => {
|
||||
Either::<_, Fallback>::Left(NestedRouteView::new(
|
||||
&outer_owner,
|
||||
new_match,
|
||||
))
|
||||
.rebuild(&mut prev);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Either::<NestedRouteView<View>, _>::Right((self
|
||||
.fallback)(
|
||||
))
|
||||
.rebuild(&mut prev);
|
||||
}
|
||||
prev
|
||||
} else {
|
||||
new_view.build()
|
||||
match new_match {
|
||||
Some(matched) => Either::Left(NestedRouteView::new(
|
||||
&outer_owner,
|
||||
matched,
|
||||
)),
|
||||
_ => Either::Right((self.fallback)()),
|
||||
}
|
||||
.build()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -175,6 +200,142 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn nested_rebuild<'a, NewMatch, R>(
|
||||
outer_owner: &Owner,
|
||||
current: &mut NestedRouteState<
|
||||
<<NewMatch::View as ChooseView>::Output as Render<R>>::State,
|
||||
>,
|
||||
new: NewMatch,
|
||||
) where
|
||||
NewMatch: MatchInterface<'a>,
|
||||
NewMatch::View: ChooseView,
|
||||
<NewMatch::View as ChooseView>::Output: Render<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
// if the new match is a different branch of the nested route tree from the current one, we can
|
||||
// just rebuild the view starting here: everything underneath it will change
|
||||
if new.as_id() != current.id {
|
||||
// TODO provide params + matched via context?
|
||||
let new_view = NestedRouteView::new(&outer_owner, new);
|
||||
let prev_owner = std::mem::replace(&mut current.owner, new_view.owner);
|
||||
current.id = new_view.id;
|
||||
current.params = new_view.params;
|
||||
current.matched = new_view.matched;
|
||||
current
|
||||
.owner
|
||||
.with(|| new_view.view.rebuild(&mut current.view));
|
||||
|
||||
// TODO is this the right place to drop the old Owner?
|
||||
drop(prev_owner);
|
||||
} else {
|
||||
tracing::warn!("TODO: replace");
|
||||
// otherwise, we should recurse to the children of the current view, and the new match
|
||||
//nested_rebuild(current.as_child_mut(), new.as_child())
|
||||
}
|
||||
|
||||
// update params, in case they're different
|
||||
// TODO
|
||||
}
|
||||
|
||||
pub struct NestedRouteView<View> {
|
||||
id: RouteMatchId,
|
||||
owner: Owner,
|
||||
params: Params,
|
||||
matched: String,
|
||||
view: View,
|
||||
}
|
||||
|
||||
impl<View> NestedRouteView<View> {
|
||||
pub fn new<'a, Matcher>(outer_owner: &Owner, matched: Matcher) -> Self
|
||||
where
|
||||
Matcher: MatchInterface<'a>,
|
||||
Matcher::View: ChooseView<Output = View>,
|
||||
{
|
||||
NestedRouteView {
|
||||
id: matched.as_id(),
|
||||
owner: outer_owner.child(),
|
||||
params: matched
|
||||
.to_params()
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect(),
|
||||
matched: matched.as_matched().to_string(),
|
||||
view: matched.to_view().choose(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, View> Render<R> for NestedRouteView<View>
|
||||
where
|
||||
View: Render<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type State = NestedRouteState<View::State>;
|
||||
type FallibleState = ();
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let NestedRouteView {
|
||||
id,
|
||||
owner,
|
||||
params,
|
||||
matched,
|
||||
view,
|
||||
} = self;
|
||||
NestedRouteState {
|
||||
id,
|
||||
owner,
|
||||
params,
|
||||
matched,
|
||||
view: view.build(),
|
||||
}
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn try_build(self) -> tachys::error::Result<Self::FallibleState> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> tachys::error::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NestedRouteState<ViewState> {
|
||||
id: RouteMatchId,
|
||||
owner: Owner,
|
||||
params: Params,
|
||||
matched: String,
|
||||
view: ViewState,
|
||||
}
|
||||
|
||||
impl<ViewState, R> Mountable<R> for NestedRouteState<ViewState>
|
||||
where
|
||||
ViewState: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
self.view.unmount();
|
||||
}
|
||||
|
||||
fn mount(&mut self, parent: &R::Element, marker: Option<&R::Node>) {
|
||||
self.view.mount(parent, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(
|
||||
&self,
|
||||
parent: &R::Element,
|
||||
child: &mut dyn Mountable<R>,
|
||||
) -> bool {
|
||||
self.view.insert_before_this(parent, child)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Rndr, Loc, FallbackFn, Fallback, Children, View> RenderHtml<Rndr>
|
||||
for Router<Rndr, Loc, Children, FallbackFn>
|
||||
where
|
||||
|
@ -239,6 +400,40 @@ where
|
|||
self
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tuples {
|
||||
($either:ident => $($ty:ident),*) => {
|
||||
paste::paste! {
|
||||
impl<$($ty, [<Fn $ty>],)*> ChooseView for $either<$([<Fn $ty>],)*>
|
||||
where
|
||||
$([<Fn $ty>]: Fn() -> $ty,)*
|
||||
{
|
||||
type Output = $either<$($ty,)*>;
|
||||
|
||||
fn choose(self) -> Self::Output {
|
||||
match self {
|
||||
$($either::$ty(f) => $either::$ty(f()),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tuples!(EitherOf3 => A, B, C);
|
||||
tuples!(EitherOf4 => A, B, C, D);
|
||||
tuples!(EitherOf5 => A, B, C, D, E);
|
||||
tuples!(EitherOf6 => A, B, C, D, E, F);
|
||||
tuples!(EitherOf7 => A, B, C, D, E, F, G);
|
||||
tuples!(EitherOf8 => A, B, C, D, E, F, G, H);
|
||||
tuples!(EitherOf9 => A, B, C, D, E, F, G, H, I);
|
||||
tuples!(EitherOf10 => A, B, C, D, E, F, G, H, I, J);
|
||||
tuples!(EitherOf11 => A, B, C, D, E, F, G, H, I, J, K);
|
||||
tuples!(EitherOf12 => A, B, C, D, E, F, G, H, I, J, K, L);
|
||||
tuples!(EitherOf13 => A, B, C, D, E, F, G, H, I, J, K, L, M);
|
||||
tuples!(EitherOf14 => A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
||||
tuples!(EitherOf15 => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
|
||||
tuples!(EitherOf16 => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
|
||||
/*
|
||||
impl<Rndr, Loc, Fal, Children> RenderHtml<Rndr>
|
||||
for Router<Rndr, Loc, Children, Fal>
|
||||
|
|
|
@ -60,12 +60,16 @@ where
|
|||
};
|
||||
|
||||
let (matched, remaining) = self.children.match_nested(path);
|
||||
#[cfg(feature = "tracing")]
|
||||
tracing::info!("matched = {:?}", matched.is_some());
|
||||
let matched = matched?;
|
||||
|
||||
if !remaining.is_empty() {
|
||||
#[cfg(feature = "tracing")]
|
||||
tracing::info!("did not match because remaining was {remaining:?}");
|
||||
None
|
||||
} else {
|
||||
Some(matched)
|
||||
Some(matched.1)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,11 +83,18 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct RouteMatchId(pub(crate) u8);
|
||||
|
||||
pub trait MatchInterface<'a> {
|
||||
type Params: IntoIterator<Item = (&'a str, &'a str)>;
|
||||
type Child;
|
||||
type View;
|
||||
|
||||
fn as_id(&self) -> RouteMatchId;
|
||||
|
||||
fn as_matched(&self) -> &str;
|
||||
|
||||
fn to_params(&self) -> Self::Params;
|
||||
|
||||
fn to_child(&'a self) -> Self::Child;
|
||||
|
@ -95,7 +106,10 @@ pub trait MatchNestedRoutes<'a> {
|
|||
type Data;
|
||||
type Match: MatchInterface<'a>;
|
||||
|
||||
fn match_nested(&'a self, path: &'a str) -> (Option<Self::Match>, &'a str);
|
||||
fn match_nested(
|
||||
&'a self,
|
||||
path: &'a str,
|
||||
) -> (Option<(RouteMatchId, Self::Match)>, &'a str);
|
||||
|
||||
fn generate_routes(
|
||||
&self,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::{
|
||||
MatchInterface, MatchNestedRoutes, PartialPathMatch, PossibleRouteMatch,
|
||||
};
|
||||
use crate::PathSegment;
|
||||
use crate::{PathSegment, RouteMatchId};
|
||||
use alloc::vec::Vec;
|
||||
use core::{fmt, iter};
|
||||
|
||||
|
@ -17,6 +17,7 @@ pub struct NestedRoute<Segments, Children, Data, View> {
|
|||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct NestedMatch<'a, ParamsIter, Child, View> {
|
||||
id: RouteMatchId,
|
||||
/// 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.
|
||||
|
@ -51,6 +52,14 @@ where
|
|||
type Child = &'a Child;
|
||||
type View = &'a View;
|
||||
|
||||
fn as_id(&self) -> RouteMatchId {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn as_matched(&self) -> &str {
|
||||
self.matched
|
||||
}
|
||||
|
||||
fn to_params(&self) -> Self::Params {
|
||||
self.params.clone()
|
||||
}
|
||||
|
@ -87,7 +96,10 @@ where
|
|||
<<Children::Match as MatchInterface<'a>>::Params as IntoIterator>::IntoIter,
|
||||
>, Children::Match, View>;
|
||||
|
||||
fn match_nested(&'a self, path: &'a str) -> (Option<Self::Match>, &'a str) {
|
||||
fn match_nested(
|
||||
&'a self,
|
||||
path: &'a str,
|
||||
) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {
|
||||
self.segments
|
||||
.test(path)
|
||||
.and_then(
|
||||
|
@ -98,17 +110,21 @@ where
|
|||
}| {
|
||||
let (inner, remaining) =
|
||||
self.children.match_nested(remaining);
|
||||
let inner = inner?;
|
||||
let (id, inner) = inner?;
|
||||
let params = params.into_iter();
|
||||
|
||||
if remaining.is_empty() {
|
||||
Some((
|
||||
Some(NestedMatch {
|
||||
Some((
|
||||
id,
|
||||
NestedMatch {
|
||||
id,
|
||||
matched,
|
||||
params: params.chain(inner.to_params()),
|
||||
child: inner,
|
||||
view: &self.view,
|
||||
}),
|
||||
},
|
||||
)),
|
||||
remaining,
|
||||
))
|
||||
} else {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{MatchInterface, MatchNestedRoutes, PathSegment};
|
||||
use crate::{MatchInterface, MatchNestedRoutes, PathSegment, RouteMatchId};
|
||||
use alloc::vec::Vec;
|
||||
use core::iter;
|
||||
use either_of::*;
|
||||
|
@ -8,6 +8,14 @@ impl<'a> MatchInterface<'a> for () {
|
|||
type Child = ();
|
||||
type View = ();
|
||||
|
||||
fn as_id(&self) -> RouteMatchId {
|
||||
RouteMatchId(0)
|
||||
}
|
||||
|
||||
fn as_matched(&self) -> &str {
|
||||
""
|
||||
}
|
||||
|
||||
fn to_params(&self) -> Self::Params {
|
||||
iter::empty()
|
||||
}
|
||||
|
@ -21,8 +29,11 @@ impl<'a> MatchNestedRoutes<'a> for () {
|
|||
type Data = ();
|
||||
type Match = ();
|
||||
|
||||
fn match_nested(&self, path: &'a str) -> (Option<Self::Match>, &'a str) {
|
||||
(Some(()), path)
|
||||
fn match_nested(
|
||||
&self,
|
||||
path: &'a str,
|
||||
) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {
|
||||
(Some((RouteMatchId(0), ())), path)
|
||||
}
|
||||
|
||||
fn generate_routes(
|
||||
|
@ -40,6 +51,14 @@ where
|
|||
type Child = A::Child;
|
||||
type View = A::View;
|
||||
|
||||
fn as_id(&self) -> RouteMatchId {
|
||||
RouteMatchId(0)
|
||||
}
|
||||
|
||||
fn as_matched(&self) -> &str {
|
||||
self.0.as_matched()
|
||||
}
|
||||
|
||||
fn to_params(&self) -> Self::Params {
|
||||
self.0.to_params()
|
||||
}
|
||||
|
@ -60,7 +79,10 @@ where
|
|||
type Data = A::Data;
|
||||
type Match = A::Match;
|
||||
|
||||
fn match_nested(&'a self, path: &'a str) -> (Option<Self::Match>, &'a str) {
|
||||
fn match_nested(
|
||||
&'a self,
|
||||
path: &'a str,
|
||||
) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {
|
||||
self.0.match_nested(path)
|
||||
}
|
||||
|
||||
|
@ -83,6 +105,20 @@ where
|
|||
type Child = Either<A::Child, B::Child>;
|
||||
type View = Either<A::View, B::View>;
|
||||
|
||||
fn as_id(&self) -> RouteMatchId {
|
||||
match self {
|
||||
Either::Left(_) => RouteMatchId(0),
|
||||
Either::Right(_) => RouteMatchId(1),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_matched(&self) -> &str {
|
||||
match self {
|
||||
Either::Left(i) => i.as_matched(),
|
||||
Either::Right(i) => i.as_matched(),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_params(&self) -> Self::Params {
|
||||
match self {
|
||||
Either::Left(i) => Either::Left(i.to_params().into_iter()),
|
||||
|
@ -113,14 +149,17 @@ where
|
|||
type Data = (A::Data, B::Data);
|
||||
type Match = Either<A::Match, B::Match>;
|
||||
|
||||
fn match_nested(&'a self, path: &'a str) -> (Option<Self::Match>, &'a str) {
|
||||
fn match_nested(
|
||||
&'a self,
|
||||
path: &'a str,
|
||||
) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {
|
||||
#[allow(non_snake_case)]
|
||||
let (A, B) = &self;
|
||||
if let (Some(matched), remaining) = A.match_nested(path) {
|
||||
return (Some(Either::Left(matched)), remaining);
|
||||
if let (Some((id, matched)), remaining) = A.match_nested(path) {
|
||||
return (Some((id, Either::Left(matched))), remaining);
|
||||
}
|
||||
if let (Some(matched), remaining) = B.match_nested(path) {
|
||||
return (Some(Either::Right(matched)), remaining);
|
||||
if let (Some((id, matched)), remaining) = B.match_nested(path) {
|
||||
return (Some((id, Either::Right(matched))), remaining);
|
||||
}
|
||||
(None, path)
|
||||
}
|
||||
|
@ -152,7 +191,7 @@ macro_rules! chain_generated {
|
|||
}
|
||||
|
||||
macro_rules! tuples {
|
||||
($either:ident => $($ty:ident),*) => {
|
||||
($either:ident => $($ty:ident = $count:expr),*) => {
|
||||
impl<'a, $($ty,)*> MatchInterface<'a> for $either <$($ty,)*>
|
||||
where
|
||||
$($ty: MatchInterface<'a>),*,
|
||||
|
@ -165,6 +204,18 @@ macro_rules! tuples {
|
|||
type Child = $either<$($ty::Child,)*>;
|
||||
type View = $either<$($ty::View,)*>;
|
||||
|
||||
fn as_id(&self) -> RouteMatchId {
|
||||
match self {
|
||||
$($either::$ty(_) => RouteMatchId($count),)*
|
||||
}
|
||||
}
|
||||
|
||||
fn as_matched(&self) -> &str {
|
||||
match self {
|
||||
$($either::$ty(i) => i.as_matched(),)*
|
||||
}
|
||||
}
|
||||
|
||||
fn to_params(&self) -> Self::Params {
|
||||
match self {
|
||||
$($either::$ty(i) => $either::$ty(i.to_params().into_iter()),)*
|
||||
|
@ -192,12 +243,15 @@ macro_rules! tuples {
|
|||
type Data = ($($ty::Data,)*);
|
||||
type Match = $either<$($ty::Match,)*>;
|
||||
|
||||
fn match_nested(&'a self, path: &'a str) -> (Option<Self::Match>, &'a str) {
|
||||
fn match_nested(&'a self, path: &'a str) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {
|
||||
#[allow(non_snake_case)]
|
||||
|
||||
let ($($ty,)*) = &self;
|
||||
$(if let (Some(matched), remaining) = $ty.match_nested(path) {
|
||||
return (Some($either::$ty(matched)), remaining);
|
||||
let mut id = 0;
|
||||
$(if let (Some((_, matched)), remaining) = $ty.match_nested(path) {
|
||||
return (Some((RouteMatchId(id), $either::$ty(matched))), remaining);
|
||||
} else {
|
||||
id += 1;
|
||||
})*
|
||||
(None, path)
|
||||
}
|
||||
|
@ -215,17 +269,17 @@ macro_rules! tuples {
|
|||
}
|
||||
}
|
||||
|
||||
tuples!(EitherOf3 => A, B, C);
|
||||
tuples!(EitherOf4 => A, B, C, D);
|
||||
tuples!(EitherOf5 => A, B, C, D, E);
|
||||
tuples!(EitherOf6 => A, B, C, D, E, F);
|
||||
tuples!(EitherOf7 => A, B, C, D, E, F, G);
|
||||
tuples!(EitherOf8 => A, B, C, D, E, F, G, H);
|
||||
tuples!(EitherOf9 => A, B, C, D, E, F, G, H, I);
|
||||
tuples!(EitherOf10 => A, B, C, D, E, F, G, H, I, J);
|
||||
tuples!(EitherOf11 => A, B, C, D, E, F, G, H, I, J, K);
|
||||
tuples!(EitherOf12 => A, B, C, D, E, F, G, H, I, J, K, L);
|
||||
tuples!(EitherOf13 => A, B, C, D, E, F, G, H, I, J, K, L, M);
|
||||
tuples!(EitherOf14 => A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
||||
tuples!(EitherOf15 => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
|
||||
tuples!(EitherOf16 => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
|
||||
tuples!(EitherOf3 => A = 0, B = 1, C = 2);
|
||||
tuples!(EitherOf4 => A = 0, B = 1, C = 2, D = 3);
|
||||
tuples!(EitherOf5 => A = 0, B = 1, C = 2, D = 3, E = 4);
|
||||
tuples!(EitherOf6 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5);
|
||||
tuples!(EitherOf7 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6);
|
||||
tuples!(EitherOf8 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7);
|
||||
tuples!(EitherOf9 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8);
|
||||
tuples!(EitherOf10 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8, J = 9);
|
||||
tuples!(EitherOf11 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8, J = 9, K = 10);
|
||||
tuples!(EitherOf12 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8, J = 9, K = 10, L = 11);
|
||||
tuples!(EitherOf13 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8, J = 9, K = 10, L = 11, M = 12);
|
||||
tuples!(EitherOf14 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8, J = 9, K = 10, L = 11, M = 12, N = 13);
|
||||
tuples!(EitherOf15 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8, J = 9, K = 10, L = 11, M = 12, N = 13, O = 14);
|
||||
tuples!(EitherOf16 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8, J = 9, K = 10, L = 11, M = 12, N = 13, O = 14, P = 15);
|
||||
|
|
|
@ -16,7 +16,7 @@ where
|
|||
B: Mountable<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
state: Either<A, B>,
|
||||
pub state: Either<A, B>,
|
||||
marker: Rndr::Placeholder,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue