initial stage for working nested route rendering

This commit is contained in:
Greg Johnston 2024-03-08 21:59:00 -05:00
parent db4c1cb4b3
commit 13cccced06
15 changed files with 591 additions and 570 deletions

View file

@ -0,0 +1,3 @@
[unstable]
build-std = ["std", "panic_abort", "core", "alloc"]
build-std-features = ["panic_immediate_abort"]

View file

@ -4,22 +4,24 @@ version = "0.1.0"
edition = "2021"
[profile.release]
codegen-units = 1
opt-level = 'z'
lto = true
codegen-units = 1
panic = "abort"
[dependencies]
console_log = "1"
log = "0.4"
leptos = { path = "../../leptos", features = ["csr", "tracing"] }
routing = { path = "../../routing", features = ["tracing"] }
#leptos_router = { path = "../../router", features = ["csr"] }
leptos = { path = "../../leptos", features = ["csr"] } #, "tracing"] }
routing = { path = "../../routing" } #, features = ["tracing"] }
#leptos_router = { path = "../../router", features = ["csr", "nightly"] }
serde = { version = "1", features = ["derive"] }
futures = "0.3"
console_error_panic_hook = "0.1.7"
tracing-subscriber = "0.3.18"
tracing-subscriber-wasm = "0.1.0"
tracing = "0.1.40"
#leptos_meta = { path = "../../meta", features = ["csr"] }
#console_error_panic_hook = "0.1.7"
#tracing-subscriber = "0.3.18"
#tracing-subscriber-wasm = "0.1.0"
#tracing = "0.1.40"
#leptos_meta = { path = "../../meta", features = ["csr", "nightly"] }
[dev-dependencies]
wasm-bindgen-test = "0.3.0"

View file

@ -12,7 +12,8 @@ use leptos::{
use log::{debug, info};
use routing::{
location::{BrowserUrl, Location},
NestedRoute, ParamSegment, RouteData, Router, Routes, StaticSegment,
MatchNestedRoutes, NestedRoute, ParamSegment, RouteData, Router, Routes,
StaticSegment,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@ -20,7 +21,39 @@ struct ExampleContext(i32);
#[component]
pub fn RouterExample() -> impl IntoView {
info!("rendering <RouterExample/>");
let router = Router::new(
BrowserUrl::new().unwrap(),
Routes::new((
NestedRoute::new(StaticSegment(""), Home),
NestedRoute::new(StaticSegment("notfound"), NotFount),
NestedRoute::new(StaticSegment("about"), About),
)),
|| "This page could not be found.",
);
view! {
<h3>"Leptos Router Bloat"</h3>
<nav>
<a href="/">"Home"</a>
" | "
<a href="/about">"About"</a>
</nav>
<br />
<main>
{router}
// un-comment the following lines and build again
// <Router>
// <Routes>
// <Route path="/" view=|cx| view! { cx, <Home /> } />
// <Route path="/about" view=|cx| view! { cx, <About /> } />
// <Route path="/*any" view=|cx| view! { cx, <NotFount /> } />
// </Routes>
// </Router>
</main>
}
}
/*info!("rendering <RouterExample/>");
// contexts are passed down through the route tree
provide_context(ExampleContext(0));
@ -28,39 +61,15 @@ pub fn RouterExample() -> impl IntoView {
let router = Router::new(
BrowserUrl::new().unwrap(),
Routes::new((
NestedRoute {
segments: StaticSegment(""),
children: (
NestedRoute {
segments: StaticSegment(""),
children: (),
data: (),
view: |_: RouteData<Dom>| "Select a contact.",
},
// TODO: fix it so empty param doesn't match here, if we reverse the order of
// these two
NestedRoute {
segments: ParamSegment("id"),
children: (),
data: (),
view: Contact,
},
),
data: (),
view: ContactList,
},
NestedRoute {
segments: StaticSegment("settings"),
children: (),
data: (),
view: Settings,
},
NestedRoute {
segments: StaticSegment("about"),
children: (),
data: (),
view: About,
},
NestedRoute::new(StaticSegment("contacts"), ContactList).child((
NestedRoute::new(StaticSegment(""), |_| "Select a contact."),
// TODO: fix it so empty param doesn't match here, if we reverse the order of
// these two
NestedRoute::new(ParamSegment("id"), Contact),
)),
//NestedRoute::new(StaticSegment(""), ContactList),
NestedRoute::new(StaticSegment("settings"), Settings),
NestedRoute::new(StaticSegment("about"), About),
)),
|| "This page could not be found.",
);
@ -78,13 +87,13 @@ pub fn RouterExample() -> impl IntoView {
<A href="settings">"Settings"</A>
<A href="redirect-home">"Redirect to Home"</A>
*/
<a href="/">"Contacts"</a>
<a href="about">"About"</a>
<a href="settings">"Settings"</a>
<a href="redirect-home">"Redirect to Home"</a>
<a href="/contacts">"Contacts"</a>
<a href="/about">"About"</a>
<a href="/settings">"Settings"</a>
<a href="/redirect-home">"Redirect to Home"</a>
</nav>
{router}
/*<Router>
<Router>
<nav>
// ordinary <a> elements can be used for client-side navigation
// using <A> has two effects:
@ -118,8 +127,20 @@ pub fn RouterExample() -> impl IntoView {
/>
</AnimatedRoutes>
</main>
</Router>*/
</Router>
}
}*/
fn Home(data: RouteData<Dom>) -> impl IntoView {
"Home Page"
}
fn About(data: RouteData<Dom>) -> impl IntoView {
"About Page"
}
fn NotFount(data: RouteData<Dom>) -> impl IntoView {
"Not Found!"
}
/*
@ -144,7 +165,7 @@ pub fn ContactRoutes() -> impl IntoView {
}
}*/
pub fn ContactList(route_data: RouteData<Dom>) -> impl IntoView {
/*pub fn ContactList(route_data: RouteData<Dom>) -> impl IntoView {
info!("rendering <ContactList/>");
// contexts are passed down through the route tree
@ -314,4 +335,4 @@ pub fn Settings(route_data: RouteData<Dom>) -> impl IntoView {
<pre>"This page is just a placeholder."</pre>
</form>
}
}
}*/

View file

@ -1,16 +1,16 @@
use leptos::*;
use router::*;
use tracing_subscriber::fmt;
use tracing_subscriber_wasm::MakeConsoleWriter;
//use tracing_subscriber::fmt;
//use tracing_subscriber_wasm::MakeConsoleWriter;
pub fn main() {
fmt()
/*fmt()
.with_writer(
MakeConsoleWriter::default()
.map_trace_level_to(tracing::Level::DEBUG),
)
.without_time()
.init();
console_error_panic_hook::set_once();
console_error_panic_hook::set_once();*/
mount_to_body(|| view! { <RouterExample/> })
}

View file

@ -93,9 +93,6 @@ impl Location for BrowserUrl {
let navigate = {
let url = self.url.clone();
move |new_url, loc| {
web_sys::console::log_1(&JsValue::from_str(
"updating URL signal",
));
url.set(new_url);
async move {
Self::complete_navigation(&loc);

View file

@ -129,6 +129,7 @@ where
Box::new(move |ev: Event| {
let ev = ev.unchecked_into::<MouseEvent>();
let origin = window().location().origin()?;
if ev.default_prevented()
|| ev.button() != 0
|| ev.meta_key()
@ -168,23 +169,18 @@ where
return Ok(());
}
let base = window()
.location()
.origin()
.map(Cow::Owned)
.unwrap_or(Cow::Borrowed(BASE));
let url = parse_with_base(href.as_str(), &base).unwrap();
let url = parse_with_base(href.as_str(), &origin).unwrap();
let path_name = unescape(&url.path);
ev.prevent_default();
// let browser handle this event if it leaves our domain
// or our base path
if url.origin != window().location().origin().unwrap_or_default()
if url.origin != origin
|| (!router_base.is_empty()
&& !path_name.is_empty()
&& !path_name
.to_lowercase()
.starts_with(&router_base.to_lowercase()))
// NOTE: the two `to_lowercase()` calls here added a total of about 14kb to
// release binary size, for limited gain
&& !path_name.starts_with(&*router_base))
{
return Ok(());
}
@ -203,8 +199,6 @@ where
}
});
ev.prevent_default();
let replace = Reflect::get(&a, &JsValue::from_str("replace"))
.ok()
.and_then(|value| value.as_bool())

View file

@ -4,6 +4,7 @@ use tachys::{renderer::Renderer, view::Render};
pub trait ChooseView<R>
where
Self: 'static,
R: Renderer,
{
type Output: Render<R>;
@ -13,7 +14,7 @@ where
impl<F, View, R> ChooseView<R> for F
where
F: Fn(RouteData<R>) -> View,
F: Fn(RouteData<R>) -> View + 'static,
View: Render<R>,
R: Renderer,
{
@ -49,4 +50,35 @@ where
}
}
// TODO add other Either implementations
macro_rules! tuples {
($either:ident => $($ty:ident),*) => {
impl<$($ty,)* Rndr> ChooseView<Rndr> for $either<$($ty,)*>
where
$($ty: ChooseView<Rndr>,)*
Rndr: Renderer,
{
type Output = $either<$($ty::Output,)*>;
fn choose(self, route_data: RouteData<Rndr>) -> Self::Output {
match self {
$($either::$ty(f) => $either::$ty(f.choose(route_data)),)*
}
}
}
};
}
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);

View file

@ -1,4 +1,5 @@
use super::{PartialPathMatch, PathSegment};
use std::borrow::Cow;
mod param_segments;
mod static_segment;
mod tuples;
@ -12,12 +13,12 @@ pub use static_segment::*;
/// as subsequent segments of the URL and tries to match them all. For a "vertical"
/// matching that sees a tuple as alternatives to one another, see [`RouteChild`](super::RouteChild).
pub trait PossibleRouteMatch {
type ParamsIter<'a>: IntoIterator<Item = (&'a str, &'a str)>;
type ParamsIter: IntoIterator<Item = (Cow<'static, str>, String)>;
fn test<'a>(
&self,
path: &'a str,
) -> Option<PartialPathMatch<'a, Self::ParamsIter<'a>>>;
) -> Option<PartialPathMatch<'a, Self::ParamsIter>>;
fn generate_path(&self, path: &mut Vec<PathSegment>);
}

View file

@ -1,16 +1,17 @@
use super::{PartialPathMatch, PathSegment, PossibleRouteMatch};
use core::iter;
use std::borrow::Cow;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ParamSegment(pub &'static str);
impl PossibleRouteMatch for ParamSegment {
type ParamsIter<'a> = iter::Once<(&'a str, &'a str)>;
type ParamsIter = iter::Once<(Cow<'static, str>, String)>;
fn test<'a>(
&self,
path: &'a str,
) -> Option<PartialPathMatch<'a, Self::ParamsIter<'a>>> {
) -> Option<PartialPathMatch<'a, Self::ParamsIter>> {
let mut matched_len = 0;
let mut param_offset = 0;
let mut param_len = 0;
@ -34,8 +35,10 @@ impl PossibleRouteMatch for ParamSegment {
}
let (matched, remaining) = path.split_at(matched_len);
let param_value =
iter::once((self.0, &path[param_offset..param_len + param_offset]));
let param_value = iter::once((
Cow::Borrowed(self.0),
path[param_offset..param_len + param_offset].to_string(),
));
Some(PartialPathMatch::new(remaining, param_value, matched))
}
@ -48,12 +51,12 @@ impl PossibleRouteMatch for ParamSegment {
pub struct WildcardSegment(pub &'static str);
impl PossibleRouteMatch for WildcardSegment {
type ParamsIter<'a> = iter::Once<(&'a str, &'a str)>;
type ParamsIter = iter::Once<(Cow<'static, str>, String)>;
fn test<'a>(
&self,
path: &'a str,
) -> Option<PartialPathMatch<'a, Self::ParamsIter<'a>>> {
) -> Option<PartialPathMatch<'a, Self::ParamsIter>> {
let mut matched_len = 0;
let mut param_offset = 0;
let mut param_len = 0;
@ -70,8 +73,10 @@ impl PossibleRouteMatch for WildcardSegment {
}
let (matched, remaining) = path.split_at(matched_len);
let param_value =
iter::once((self.0, &path[param_offset..param_len + param_offset]));
let param_value = iter::once((
Cow::Borrowed(self.0),
path[param_offset..param_len + param_offset].to_string(),
));
Some(PartialPathMatch::new(remaining, param_value, matched))
}

View file

@ -1,13 +1,14 @@
use super::{PartialPathMatch, PathSegment, PossibleRouteMatch};
use core::iter;
use std::borrow::Cow;
impl PossibleRouteMatch for () {
type ParamsIter<'a> = iter::Empty<(&'a str, &'a str)>;
type ParamsIter = iter::Empty<(Cow<'static, str>, String)>;
fn test<'a>(
&self,
path: &'a str,
) -> Option<PartialPathMatch<'a, Self::ParamsIter<'a>>> {
) -> Option<PartialPathMatch<'a, Self::ParamsIter>> {
Some(PartialPathMatch::new(path, iter::empty(), ""))
}
@ -18,12 +19,12 @@ impl PossibleRouteMatch for () {
pub struct StaticSegment(pub &'static str);
impl PossibleRouteMatch for StaticSegment {
type ParamsIter<'a> = iter::Empty<(&'a str, &'a str)>;
type ParamsIter = iter::Empty<(Cow<'static, str>, String)>;
fn test<'a>(
&self,
path: &'a str,
) -> Option<PartialPathMatch<'a, Self::ParamsIter<'a>>> {
) -> Option<PartialPathMatch<'a, Self::ParamsIter>> {
let mut matched_len = 0;
let mut test = path.chars().peekable();
let mut this = self.0.chars();

View file

@ -5,14 +5,14 @@ macro_rules! chain_types {
($first:ty, $second:ty, ) => {
Chain<
$first,
<<$second as PossibleRouteMatch>::ParamsIter<'a> as IntoIterator>::IntoIter
<<$second as PossibleRouteMatch>::ParamsIter as IntoIterator>::IntoIter
>
};
($first:ty, $second:ty, $($rest:ty,)+) => {
chain_types!(
Chain<
$first,
<<$second as PossibleRouteMatch>::ParamsIter<'a> as IntoIterator>::IntoIter,
<<$second as PossibleRouteMatch>::ParamsIter as IntoIterator>::IntoIter,
>,
$($rest,)+
)
@ -27,9 +27,9 @@ macro_rules! tuples {
$first: PossibleRouteMatch,
$($ty: PossibleRouteMatch),*,
{
type ParamsIter<'a> = chain_types!(<<$first>::ParamsIter<'a> as IntoIterator>::IntoIter, $($ty,)*);
type ParamsIter = chain_types!(<<$first>::ParamsIter as IntoIterator>::IntoIter, $($ty,)*);
fn test<'a>(&self, path: &'a str) -> Option<PartialPathMatch<'a, Self::ParamsIter<'a>>> {
fn test<'a>(&self, path: &'a str) -> Option<PartialPathMatch<'a, Self::ParamsIter>> {
let mut matched_len = 0;
#[allow(non_snake_case)]
let ($first, $($ty,)*) = &self;

View file

@ -10,7 +10,7 @@ pub use nested::*;
use std::{borrow::Cow, marker::PhantomData};
use tachys::{
renderer::Renderer,
view::{any_view::IntoAny, Render},
view::{any_view::IntoAny, Render, RenderHtml},
};
pub use vertical::*;
@ -47,10 +47,7 @@ where
Rndr: Renderer + 'static,
Children: MatchNestedRoutes<Rndr>,
{
pub fn match_route<'a>(
&'a self,
path: &'a str,
) -> Option<Children::Match<'a>> {
pub fn match_route(&self, path: &str) -> Option<Children::Match> {
let path = match &self.base {
None => path,
Some(base) => {
@ -89,41 +86,40 @@ where
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct RouteMatchId(pub(crate) u8);
pub trait MatchInterface<'a, R>
pub trait MatchInterface<R>
where
R: Renderer,
R: Renderer + 'static,
{
type Params: IntoIterator<Item = (&'a str, &'a str)>;
type Child: MatchInterface<'a, R>;
type View: Render<R>;
type Child: MatchInterface<R> + MatchParams + 'static;
type View: Render<R> + RenderHtml<R> + 'static;
fn as_id(&self) -> RouteMatchId;
fn as_matched(&self) -> &str;
fn to_params(&self) -> Self::Params;
fn into_view_and_child(
self,
) -> (
impl ChooseView<R, Output = Self::View> + 'a,
Option<Self::Child>,
);
) -> (impl ChooseView<R, Output = Self::View>, Option<Self::Child>);
}
pub trait MatchParams {
type Params: IntoIterator<Item = (Cow<'static, str>, String)>;
fn to_params(&self) -> Self::Params;
}
pub trait MatchNestedRoutes<R>
where
R: Renderer,
R: Renderer + 'static,
{
type Data;
type Match<'a>: MatchInterface<'a, R>
where
Self: 'a;
type View;
type Match: MatchInterface<R> + MatchParams;
fn match_nested<'a>(
&'a self,
path: &'a str,
) -> (Option<(RouteMatchId, Self::Match<'a>)>, &str);
) -> (Option<(RouteMatchId, Self::Match)>, &str);
fn generate_routes(
&self,

View file

@ -2,10 +2,13 @@ use super::{
MatchInterface, MatchNestedRoutes, PartialPathMatch, PathSegment,
PossibleRouteMatch, RouteMatchId,
};
use crate::{ChooseView, RouteData};
use crate::{ChooseView, MatchParams, RouteData};
use core::{fmt, iter};
use std::marker::PhantomData;
use tachys::{renderer::Renderer, view::Render};
use std::{borrow::Cow, marker::PhantomData};
use tachys::{
renderer::Renderer,
view::{Render, RenderHtml},
};
mod tuples;
@ -18,20 +21,58 @@ pub struct NestedRoute<Segments, Children, Data, ViewFn, R> {
pub rndr: PhantomData<R>,
}
impl<Segments, ViewFn, R> NestedRoute<Segments, (), (), ViewFn, R> {
pub fn new<View>(path: Segments, view: ViewFn) -> Self
where
ViewFn: Fn(RouteData<R>) -> View,
R: Renderer,
{
Self {
segments: path,
children: (),
data: (),
view,
rndr: PhantomData,
}
}
}
impl<Segments, Data, ViewFn, R> NestedRoute<Segments, (), Data, ViewFn, R> {
pub fn child<Children>(
self,
child: Children,
) -> NestedRoute<Segments, Children, Data, ViewFn, R> {
let Self {
segments,
data,
view,
rndr,
..
} = self;
NestedRoute {
segments,
children: child,
data,
view,
rndr,
}
}
}
#[derive(PartialEq, Eq)]
pub struct NestedMatch<'a, ParamsIter, Child, ViewFn> {
pub struct NestedMatch<ParamsIter, Child, ViewFn> {
id: RouteMatchId,
/// The portion of the full path matched only by this nested route.
matched: &'a str,
matched: String,
/// The map of params matched only by this nested route.
params: ParamsIter,
/// The nested route.
child: Child,
view_fn: &'a ViewFn,
view_fn: ViewFn,
}
impl<'a, ParamsIter, Child, ViewFn> fmt::Debug
for NestedMatch<'a, ParamsIter, Child, ViewFn>
impl<ParamsIter, Child, ViewFn> fmt::Debug
for NestedMatch<ParamsIter, Child, ViewFn>
where
ParamsIter: fmt::Debug,
Child: fmt::Debug,
@ -45,16 +86,27 @@ where
}
}
impl<'a, ParamsIter, Child, ViewFn, Rndr> MatchInterface<'a, Rndr>
for NestedMatch<'a, ParamsIter, Child, ViewFn>
impl<ParamsIter, Child, ViewFn> MatchParams
for NestedMatch<ParamsIter, Child, ViewFn>
where
Rndr: Renderer + 'static,
ParamsIter: IntoIterator<Item = (&'a str, &'a str)> + Clone,
Child: MatchInterface<'a, Rndr>,
ViewFn: Fn(RouteData<Rndr>),
ViewFn::Output: Render<Rndr>,
ParamsIter: IntoIterator<Item = (Cow<'static, str>, String)> + Clone,
{
type Params = ParamsIter;
#[inline(always)]
fn to_params(&self) -> Self::Params {
self.params.clone()
}
}
impl<ParamsIter, Child, ViewFn, View, Rndr> MatchInterface<Rndr>
for NestedMatch<ParamsIter, Child, ViewFn>
where
Rndr: Renderer + 'static,
Child: MatchInterface<Rndr> + MatchParams + 'static,
ViewFn: Fn(RouteData<Rndr>) -> View + 'static,
View: Render<Rndr> + RenderHtml<Rndr> + 'static,
{
type Child = Child;
type View = ViewFn::Output;
@ -63,50 +115,44 @@ where
}
fn as_matched(&self) -> &str {
self.matched
}
fn to_params(&self) -> Self::Params {
self.params.clone()
&self.matched
}
fn into_view_and_child(
self,
) -> (
impl ChooseView<Rndr, Output = Self::View> + 'a,
impl ChooseView<Rndr, Output = Self::View>,
Option<Self::Child>,
) {
(self.view_fn, Some(self.child))
}
}
impl<'a, ParamsIter, Child, ViewFn> NestedMatch<'a, ParamsIter, Child, ViewFn> {
pub fn matched(&self) -> &'a str {
self.matched
}
}
impl<Segments, Children, Data, ViewFn, Rndr> MatchNestedRoutes<Rndr>
impl<Segments, Children, Data, ViewFn, View, Rndr> MatchNestedRoutes<Rndr>
for NestedRoute<Segments, Children, Data, ViewFn, Rndr>
where
Rndr: Renderer + 'static,
Segments: PossibleRouteMatch,
<<Segments as PossibleRouteMatch>::ParamsIter as IntoIterator>::IntoIter: Clone,
Children: MatchNestedRoutes<Rndr>,
for<'a> <Segments::ParamsIter<'a> as IntoIterator>::IntoIter: Clone,
for <'a> <<Children::Match<'a> as MatchInterface<'a, Rndr>>::Params as IntoIterator>::IntoIter:
Clone,
ViewFn: Fn(RouteData<Rndr>),
<<<Children as MatchNestedRoutes<Rndr>>::Match as MatchParams>::Params as IntoIterator>::IntoIter: Clone,
Children::Match: MatchParams,
Children: 'static,
<Children::Match as MatchParams>::Params: Clone,
ViewFn: Fn(RouteData<Rndr>) -> View + Clone + 'static,
View: Render<Rndr> + RenderHtml<Rndr> + 'static,
{
type Data = Data;
type Match<'a> = NestedMatch<'a, iter::Chain<
<Segments::ParamsIter<'a> as IntoIterator>::IntoIter,
<<Children::Match<'a> as MatchInterface<'a, Rndr>>::Params as IntoIterator>::IntoIter,
>, Children::Match<'a>, ViewFn> where <Children as MatchNestedRoutes<Rndr>>::Match<'a>: 'a, ViewFn: 'a, Children: 'a, Segments: 'a, Data: 'a;
type View = View;
type Match = NestedMatch<iter::Chain<
<Segments::ParamsIter as IntoIterator>::IntoIter,
<<Children::Match as MatchParams>::Params as IntoIterator>::IntoIter,
>, Children::Match, ViewFn>;
fn match_nested<'a>(
&'a self,
path: &'a str,
) -> (Option<(RouteMatchId, Self::Match<'a>)>, &'a str) {
) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {
self.segments
.test(path)
.and_then(
@ -126,10 +172,10 @@ where
id,
NestedMatch {
id,
matched,
matched: matched.to_string(),
params: params.chain(inner.to_params()),
child: inner,
view_fn: &self.view,
view_fn: self.view.clone(),
},
)),
remaining,

View file

@ -1,14 +1,22 @@
use super::{MatchInterface, MatchNestedRoutes, PathSegment, RouteMatchId};
use crate::ChooseView;
use crate::{ChooseView, MatchParams};
use core::iter;
use either_of::*;
use std::borrow::Cow;
use tachys::renderer::Renderer;
impl<'a, Rndr> MatchInterface<'a, Rndr> for ()
impl MatchParams for () {
type Params = iter::Empty<(Cow<'static, str>, String)>;
fn to_params(&self) -> Self::Params {
iter::empty()
}
}
impl<Rndr> MatchInterface<Rndr> for ()
where
Rndr: Renderer,
Rndr: Renderer + 'static,
{
type Params = iter::Empty<(&'a str, &'a str)>;
type Child = ();
type View = ();
@ -20,14 +28,10 @@ where
""
}
fn to_params(&self) -> Self::Params {
iter::empty()
}
fn into_view_and_child(
self,
) -> (
impl ChooseView<Rndr, Output = Self::View> + 'a,
impl ChooseView<Rndr, Output = Self::View>,
Option<Self::Child>,
) {
((), None)
@ -36,15 +40,16 @@ where
impl<Rndr> MatchNestedRoutes<Rndr> for ()
where
Rndr: Renderer,
Rndr: Renderer + 'static,
{
type Data = ();
type Match<'a> = ();
type View = ();
type Match = ();
fn match_nested<'a>(
&self,
path: &'a str,
) -> (Option<(RouteMatchId, Self::Match<'a>)>, &'a str) {
) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {
(Some((RouteMatchId(0), ())), path)
}
@ -55,12 +60,22 @@ where
}
}
impl<'a, A, Rndr> MatchInterface<'a, Rndr> for (A,)
impl<A> MatchParams for (A,)
where
A: MatchInterface<'a, Rndr>,
Rndr: Renderer,
A: MatchParams,
{
type Params = A::Params;
fn to_params(&self) -> Self::Params {
self.0.to_params()
}
}
impl<A, Rndr> MatchInterface<Rndr> for (A,)
where
A: MatchInterface<Rndr>,
Rndr: Renderer + 'static,
{
type Child = A::Child;
type View = A::View;
@ -72,14 +87,10 @@ where
self.0.as_matched()
}
fn to_params(&self) -> Self::Params {
self.0.to_params()
}
fn into_view_and_child(
self,
) -> (
impl ChooseView<Rndr, Output = Self::View> + 'a,
impl ChooseView<Rndr, Output = Self::View>,
Option<Self::Child>,
) {
self.0.into_view_and_child()
@ -89,15 +100,16 @@ where
impl<A, Rndr> MatchNestedRoutes<Rndr> for (A,)
where
A: MatchNestedRoutes<Rndr>,
Rndr: Renderer,
Rndr: Renderer + 'static,
{
type Data = A::Data;
type Match<'a> = A::Match<'a> where A: 'a;
type View = A::View;
type Match = A::Match;
fn match_nested<'a>(
&'a self,
path: &'a str,
) -> (Option<(RouteMatchId, Self::Match<'a>)>, &'a str) {
) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {
self.0.match_nested(path)
}
@ -108,16 +120,30 @@ where
}
}
impl<'a, A, B, Rndr> MatchInterface<'a, Rndr> for Either<A, B>
impl<A, B> MatchParams for Either<A, B>
where
Rndr: Renderer,
A: MatchInterface<'a, Rndr>,
B: MatchInterface<'a, Rndr>,
A: MatchParams,
B: MatchParams,
{
type Params = Either<
<A::Params as IntoIterator>::IntoIter,
<B::Params as IntoIterator>::IntoIter,
>;
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()),
}
}
}
impl<A, B, Rndr> MatchInterface<Rndr> for Either<A, B>
where
Rndr: Renderer + 'static,
A: MatchInterface<Rndr>,
B: MatchInterface<Rndr>,
{
type Child = Either<A::Child, B::Child>;
type View = Either<A::View, B::View>;
@ -135,17 +161,10 @@ where
}
}
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 into_view_and_child(
self,
) -> (
impl ChooseView<Rndr, Output = Self::View> + 'a,
impl ChooseView<Rndr, Output = Self::View>,
Option<Self::Child>,
) {
match self {
@ -168,12 +187,13 @@ where
Rndr: Renderer + 'static,
{
type Data = (A::Data, B::Data);
type Match<'a> = Either<A::Match<'a>, B::Match<'a>> where A: 'a, B: 'a;
type View = Either<A::View, B::View>;
type Match = Either<A::Match, B::Match>;
fn match_nested<'a>(
&'a self,
path: &'a str,
) -> (Option<(RouteMatchId, Self::Match<'a>)>, &'a str) {
) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {
#[allow(non_snake_case)]
let (A, B) = &self;
if let (Some((id, matched)), remaining) = A.match_nested(path) {
@ -213,15 +233,26 @@ macro_rules! chain_generated {
macro_rules! tuples {
($either:ident => $($ty:ident = $count:expr),*) => {
impl<'a, Rndr, $($ty,)*> MatchInterface<'a, Rndr> for $either <$($ty,)*>
impl<'a, $($ty,)*> MatchParams for $either <$($ty,)*>
where
Rndr: Renderer + 'static,
$($ty: MatchInterface<'a, Rndr>),*,
$($ty::Child: 'a),*,
$($ty: MatchParams),*,
{
type Params = $either<$(
<$ty::Params as IntoIterator>::IntoIter,
)*>;
fn to_params(&self) -> Self::Params {
match self {
$($either::$ty(i) => $either::$ty(i.to_params().into_iter()),)*
}
}
}
impl<Rndr, $($ty,)*> MatchInterface<Rndr> for $either <$($ty,)*>
where
Rndr: Renderer + 'static,
$($ty: MatchInterface<Rndr>),*,
{
type Child = $either<$($ty::Child,)*>;
type View = $either<$($ty::View,)*>;
@ -237,35 +268,31 @@ macro_rules! tuples {
}
}
fn to_params(&self) -> Self::Params {
fn into_view_and_child(
self,
) -> (
impl ChooseView<Rndr, Output = Self::View>,
Option<Self::Child>,
) {
match self {
$($either::$ty(i) => $either::$ty(i.to_params().into_iter()),)*
}
}
fn into_child(self) -> Option<Self::Child> {
Some(match self {
$($either::$ty(i) => $either::$ty(i.into_child()?),)*
})
}
fn to_view(&self) -> impl ChooseView<Rndr, Output = Self::View> {
match self {
$($either::$ty(i) => $either::$ty(i.to_view()),)*
$($either::$ty(i) => {
let (view, child) = i.into_view_and_child();
($either::$ty(view), child.map($either::$ty))
})*
}
}
}
impl<'a, Rndr, $($ty),*> MatchNestedRoutes<'a, Rndr> for ($($ty,)*)
impl<Rndr, $($ty),*> MatchNestedRoutes<Rndr> for ($($ty,)*)
where
Rndr: Renderer + 'static,
$($ty: MatchNestedRoutes<'a, Rndr>),*,
$($ty::Match: 'a),*,
$($ty: MatchNestedRoutes<Rndr>),*,
{
type Data = ($($ty::Data,)*);
type View = $either<$($ty::View,)*>;
type Match = $either<$($ty::Match,)*>;
fn match_nested(&'a self, path: &'a str) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {
fn match_nested<'a>(&'a self, path: &'a str) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {
#[allow(non_snake_case)]
let ($($ty,)*) = &self;
@ -290,9 +317,9 @@ macro_rules! tuples {
}
}
}
/*
tuples!(EitherOf3 => A = 0, B = 1, C = 2);
tuples!(EitherOf4 => A = 0, B = 1, C = 2, D = 3);
/*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);

View file

@ -5,7 +5,7 @@ use crate::{
MatchInterface, MatchNestedRoutes, PossibleRouteMatch, RouteMatchId,
Routes,
},
ChooseView, Params,
ChooseView, MatchParams, Params,
};
use core::marker::PhantomData;
use either_of::*;
@ -45,6 +45,7 @@ where
Rndr: Renderer,
FallbackFn: Fn() -> Fallback,
{
#[inline(always)]
pub fn new(
location: Loc,
routes: Routes<Children, Rndr>,
@ -58,6 +59,7 @@ where
}
}
#[inline(always)]
pub fn new_with_base(
base: impl Into<Cow<'static, str>>,
location: Loc,
@ -99,13 +101,19 @@ where
FallbackFn: Fn() -> Fallback + 'static,
Fallback: Render<Rndr>,
Children: MatchNestedRoutes<Rndr> + 'static,
//for<'a> <Children::Match<'a> as MatchInterface<Rndr, View = View>>,
/*View: Render<Rndr> + IntoAny<Rndr> + 'static,
View::State: 'static,*/
Fallback::State: 'static,
Rndr: Renderer + 'static,
{
type State =
RenderEffect<EitherState<(), <Fallback as Render<Rndr>>::State, Rndr>>;
type State = RenderEffect<
EitherState<
<NestedRouteView<Children::Match, Rndr> as Render<Rndr>>::State,
<Fallback as Render<Rndr>>::State,
Rndr,
>,
>;
type FallibleState = ();
fn build(self) -> Self::State {
@ -130,33 +138,32 @@ where
if let Some(new_match) = new_match {
match &mut prev.state {
Either::Left(prev) => {
// TODO!
//nested_rebuild(&outer_owner, prev, new_match);
}
Either::Right(_) => {
/*Either::<_, Fallback>::Left(NestedRouteView::new(
&outer_owner,
new_match,
))
.rebuild(&mut prev);*/
Either::<_, Fallback>::Left(
NestedRouteView::create(
&outer_owner,
new_match,
),
)
.rebuild(&mut prev);
}
}
} else {
/*Either::<NestedRouteView<View, Rndr>, _>::Right((self
.fallback)(
))
.rebuild(&mut prev);*/
Either::<NestedRouteView<Children::Match, Rndr>, _>::Right(
(self.fallback)(),
)
.rebuild(&mut prev);
}
prev
} else {
match new_match {
Some(matched) =>
/*Either::Left(NestedRouteView::new(
Some(matched) => Either::Left(NestedRouteView::create(
&outer_owner,
matched,
))*/
{
Either::Left(())
}
)),
_ => Either::Right((self.fallback)()),
}
.build()
@ -178,65 +185,155 @@ where
}
}
/*fn nested_rebuild<NewMatch, R>(
outer_owner: &Owner,
current: &mut NestedRouteState<
<<NewMatch::View as ChooseView<R>>::Output as Render<R>>::State,
>,
new: NewMatch,
) where
NewMatch: MatchInterface<R>,
NewMatch::View: ChooseView<R>,
<NewMatch::View as ChooseView<R>>::Output: Render<R> + IntoAny<R> + 'static,
NewMatch::Child: std::fmt::Debug,
R: Renderer + 'static,
{
// 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 {
// 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, R>
pub struct NestedRouteView<Matcher, R>
where
R: Renderer,
Matcher: MatchInterface<R>,
R: Renderer + 'static,
{
id: RouteMatchId,
owner: Owner,
params: ArcRwSignal<Params>,
matched: ArcRwSignal<String>,
view: View,
rndr: PhantomData<R>,
view: Matcher::View,
//child: Option<Box<dyn FnOnce() -> NestedRouteView<Matcher::Child, R>>>,
ty: PhantomData<(Matcher, R)>,
}
impl<View, R> NestedRouteView<View, R>
impl<Matcher, Rndr> NestedRouteView<Matcher, Rndr>
where
Matcher: MatchInterface<Rndr> + MatchParams,
Matcher::Child: 'static,
Matcher::View: 'static,
Rndr: Renderer + 'static,
{
pub fn create(outer_owner: &Owner, route_match: Matcher) -> Self {
let id = route_match.as_id();
let owner = outer_owner.child();
let params = ArcRwSignal::new(
route_match
.to_params()
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect(),
);
let matched = ArcRwSignal::new(route_match.as_matched().to_string());
/*let (view, child) = route_match.into_view_and_child();
let child = child.map(|child| {
let owner = owner.clone();
Box::new(move || NestedRouteView::create(&owner, child))
as Box<dyn FnOnce() -> NestedRouteView<Matcher::Child, Rndr>>
});*/
let view = build_nested(route_match);
Self {
id,
owner,
params,
matched,
view,
ty: PhantomData,
}
}
}
pub struct NestedRouteState<Matcher, Rndr>
where
Matcher: MatchInterface<Rndr>,
Rndr: Renderer + 'static,
{
id: RouteMatchId,
owner: Owner,
params: ArcRwSignal<Params>,
matched: ArcRwSignal<String>,
view: <Matcher::View as Render<Rndr>>::State,
//child: Option<Box<NestedRouteState<Matcher::Child, Rndr>>>,
}
// TODO: also build a Vec<(RouteMatchId, ArcRwSignal<Params>)>
// when we rebuild, at each level,
// if the route IDs don't match, then replace with new
// if they do match, then just update params
fn build_nested<Match, R>(route_match: Match) -> Match::View
where
Match: MatchInterface<R>,
R: Renderer,
{
let (view, child) = route_match.into_view_and_child();
let outlet = move || child.map(|child| build_nested(child)).into_any();
let data = RouteData {
params: { ArcMemo::new(move |_| Params::new()) },
outlet: Box::new(outlet),
};
view.choose(data)
}
impl<Matcher, R> Render<R> for NestedRouteView<Matcher, R>
where
Matcher: MatchInterface<R>,
Matcher::View: Sized + 'static,
R: Renderer + 'static,
{
type State = NestedRouteState<Matcher, R>;
type FallibleState = ();
fn build(self) -> Self::State {
NestedRouteState {
id: self.id,
owner: self.owner,
params: self.params,
matched: self.matched,
view: self.view.build(), //child: None,
}
}
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!()
}
}
impl<Matcher, R> Mountable<R> for NestedRouteState<Matcher, R>
where
Matcher: MatchInterface<R>,
R: Renderer + 'static,
{
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<View, R> NestedRouteView<View, R>
where
R: Renderer + 'static,
{
pub fn new<Matcher>(outer_owner: &Owner, route_match: Matcher) -> Self
where
Matcher: for<'a> MatchInterface<'a, R, View = View> + 'static,
for<'a> <Matcher as MatchInterface<'a, R>>::View:
Matcher: for<'a> MatchInterface<R, View = View> + 'static,
for<'a> <Matcher as MatchInterface<R>>::View:
ChooseView<R, Output = View>,
for<'a> <Matcher as MatchInterface<'a, R>>::Child: std::fmt::Debug,
for<'a> <Matcher as MatchInterface<R>>::Child: std::fmt::Debug,
View: IntoAny<R> + 'static,
{
let params = ArcRwSignal::new(
@ -281,83 +378,104 @@ where
rndr: PhantomData,
}
}
}
}*/
impl<R, View> Render<R> for NestedRouteView<View, R>
where
View: Render<R>,
R: Renderer,
/*fn nested_rebuild<NewMatch, R>(
outer_owner: &Owner,
current: &mut NestedRouteState<
<<NewMatch::View as ChooseView<R>>::Output as Render<R>>::State,
>,
new: NewMatch,
) where
NewMatch: MatchInterface<R>,
NewMatch::View: ChooseView<R>,
<NewMatch::View as ChooseView<R>>::Output: Render<R> + IntoAny<R> + 'static,
NewMatch::Child: std::fmt::Debug,
R: Renderer + 'static,
{
type State = NestedRouteState<View::State>;
type FallibleState = ();
// 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));
fn build(self) -> Self::State {
let NestedRouteView {
// TODO is this the right place to drop the old Owner?
drop(prev_owner);
} else {
// 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
}*/
/*impl<View, R> NestedRouteView<View, R>
where
R: Renderer + 'static,
{
pub fn new<Matcher>(outer_owner: &Owner, route_match: Matcher) -> Self
where
Matcher: for<'a> MatchInterface<R, View = View> + 'static,
for<'a> <Matcher as MatchInterface<R>>::View:
ChooseView<R, Output = View>,
for<'a> <Matcher as MatchInterface<R>>::Child: std::fmt::Debug,
View: IntoAny<R> + 'static,
{
let params = ArcRwSignal::new(
route_match
.to_params()
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect(),
);
let matched = ArcRwSignal::new(route_match.as_matched().to_string());
let id = route_match.as_id();
let (view, child) = route_match.into_view_and_child();
let route_data = RouteData {
params: {
let params = params.clone();
ArcMemo::new(move |_| params.get())
},
outlet: Box::new({
move || {
child
.map(|child| {
// TODO nest the next child and use real params
/*let params = child.to_params().into_iter().map(|(k, v)| (k.to_string(), v.to_string())).collect::<Params>();
let route_data = RouteData {
params: ArcMemo::new(move |_| {
params.clone()
}),
outlet: Box::new(|| ().into_any())
};*/
format!("{child:?}")
})
.into_any()
}
}),
};
NestedRouteView {
id,
owner,
owner: outer_owner.child(),
params,
matched,
view,
rndr,
} = self;
NestedRouteState {
id,
owner,
params,
matched,
view: view.build(),
view: view.choose(route_data),
rndr: PhantomData,
}
}
}*/
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: ArcRwSignal<Params>,
matched: ArcRwSignal<String>,
view: ViewState,
}
impl<ViewState, R> Mountable<R> for NestedRouteState<ViewState>
trait RouteView<R>: for<'a> MatchInterface<R>
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)
}
}
trait RouteView<R>: for<'a> MatchInterface<'a, R>
where
R: Renderer,
R: Renderer + 'static,
{
type RouteViewChild: RouteView<R>;
type RouteView: Render<R>;
@ -365,23 +483,16 @@ where
fn into_child(self) -> Option<Self::RouteViewChild>;
}
impl<Rndr, Loc, FallbackFn, Fallback, Children, View> RenderHtml<Rndr>
impl<Rndr, Loc, FallbackFn, Fallback, Children> RenderHtml<Rndr>
for Router<Rndr, Loc, Children, FallbackFn>
where
Loc: Location,
FallbackFn: Fn() -> Fallback + 'static,
Fallback: RenderHtml<Rndr>,
Children: MatchNestedRoutes<Rndr> + 'static,
for<'a> <<Children as MatchNestedRoutes<Rndr>>::Match<'a> as MatchInterface<
'a,
Rndr,
>>::View: ChooseView<Rndr, Output = View>,
for<'a> <<Children as MatchNestedRoutes<Rndr>>::Match<'a> as MatchInterface<
'a,
Rndr,
>>::Child: std::fmt::Debug,
View: Render<Rndr> + IntoAny<Rndr> + 'static,
View::State: 'static,
Children::View: RenderHtml<Rndr>,
/*View: Render<Rndr> + IntoAny<Rndr> + 'static,
View::State: 'static,*/
Fallback::State: 'static,
Rndr: Renderer + 'static,
{
@ -408,11 +519,10 @@ where
FallbackFn: Fn() -> Fallback,
Fallback: Render<Rndr>,
Children: MatchNestedRoutes<Rndr>,
for<'a> <<Children as MatchNestedRoutes<Rndr>>::Match<'a> as MatchInterface<
'a,
<<Children as MatchNestedRoutes<Rndr>>::Match as MatchInterface<
Rndr,
>>::View: ChooseView<Rndr, Output = View>,
Rndr: Renderer,
Rndr: Renderer + 'static,
Router<Rndr, Loc, Children, FallbackFn>: RenderHtml<Rndr>,
{
type Output<SomeNewAttr: Attribute<Rndr>> = Self;
@ -437,217 +547,3 @@ where
self
}
}
macro_rules! tuples {
($either:ident => $($ty:ident),*) => {
paste::paste! {
impl<Rndr, $($ty, [<Fn $ty>],)*> ChooseView<Rndr> for $either<$([<Fn $ty>],)*>
where
Rndr: Renderer,
$([<Fn $ty>]: Fn(RouteData<Rndr>) -> $ty,)*
$($ty: Render<Rndr>,)*
{
type Output = $either<$($ty,)*>;
fn choose(self, route_data: RouteData<Rndr>) -> Self::Output {
match self {
$($either::$ty(f) => $either::$ty(f(route_data)),)*
}
}
}
}
}
}
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>
where
Self: FallbackOrViewHtml,
Rndr: Renderer,
Loc: Location,
Children: PossibleRouteMatch,
<Self as FallbackOrView>::Output: RenderHtml<Rndr>,
Rndr::Element: Clone,
Rndr::Node: Clone,
{
const MIN_LENGTH: usize = <Self as FallbackOrViewHtml>::MIN_LENGTH;
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {
if RouteList::is_generating() {
let routes = RouteList::default();
RouteList::register(routes);
} else {
self.fallback_or_view().1.to_html_with_buf(buf, position);
}
}
fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
self,
buf: &mut StreamBuilder,
position: &mut Position,
) where
Self: Sized,
{
if RouteList::is_generating() {
let routes = RouteList::default();
RouteList::register(routes);
} else {
self.fallback_or_view()
.1
.to_html_async_with_buf::<OUT_OF_ORDER>(buf, position);
}
}
fn hydrate<const FROM_SERVER: bool>(
self,
cursor: &Cursor<Rndr>,
position: &PositionState,
) -> Self::State {
self.fallback_or_view()
.1
.hydrate::<FROM_SERVER>(cursor, position)
}
}
pub trait FallbackOrView {
type Output;
fn fallback_or_view(&self) -> (&'static str, Self::Output);
fn generate_route_list(&self, route_list: &mut RouteList);
}
pub trait FallbackOrViewHtml: FallbackOrView {
const MIN_LENGTH: usize;
}
impl<Rndr, Loc, FallbackFn, Fal> FallbackOrView
for Router<Rndr, Loc, (), FallbackFn>
where
Rndr: Renderer,
Loc: Location,
FallbackFn: Fn() -> Fal,
Fal: Render<Rndr>,
{
type Output = Fal;
fn fallback_or_view(&self) -> (&'static str, Self::Output) {
("Fal", (self.fallback)())
}
fn generate_route_list(&self, _route_list: &mut RouteList) {}
}
impl<Rndr, Loc, FallbackFn, Fal> FallbackOrViewHtml
for Router<Rndr, Loc, (), FallbackFn>
where
Rndr: Renderer,
Loc: Location,
FallbackFn: Fn() -> Fal,
Fal: RenderHtml<Rndr>,
Rndr::Element: Clone,
Rndr::Node: Clone,
{
const MIN_LENGTH: usize = Fal::MIN_LENGTH;
}
impl<Rndr, Loc, FallbackFn, Fal, APat, AViewFn, AView, AChildren> FallbackOrView
for Router<
Rndr,
Loc,
RouteDefinition<Rndr, APat, AViewFn, AChildren>,
FallbackFn,
>
where
Rndr: Renderer,
Loc: Location,
APat: RouteMatch,
AViewFn: Fn(MatchedRoute) -> AView,
AView: Render<Rndr>,
FallbackFn: Fn() -> Fal,
Fal: Render<Rndr>,
{
type Output = Either<Fal, AView>;
fn fallback_or_view(&self) -> (&'static str, Self::Output) {
match self.location.try_to_url() {
Ok(url) => {
if self.routes.path.matches(&url.pathname) {
let PartialPathMatch {
params,
matched,
remaining,
} = self.routes.path.test(&url.pathname).unwrap();
if remaining.is_empty() {
let matched = MatchedRoute {
params,
matched,
search_params: url.search_params.clone(),
};
return (
"Route",
Either::Right(self.routes.view(matched)),
);
}
}
("Fal", Either::Left(self.fallback()))
}
Err(e) => {
#[cfg(feature = "tracing")]
{
tracing::error!(
"Error converting location into URL: {e:?}"
);
}
("Fal", Either::Left(self.fallback()))
}
}
}
fn generate_route_list(&self, route_list: &mut RouteList) {
let mut path = Vec::new();
self.routes.path.generate_path(&mut path);
route_list.push(RouteListing::from_path(path));
}
}
impl<Rndr, Loc, FallbackFn, Fal, APat, AViewFn, AView, AChildren>
FallbackOrViewHtml
for Router<
Rndr,
Loc,
RouteDefinition<Rndr, APat, AViewFn, AChildren>,
FallbackFn,
>
where
Rndr: Renderer,
Loc: Location,
APat: RouteMatch,
AViewFn: Fn(MatchedRoute) -> AView,
AView: RenderHtml<Rndr>,
FallbackFn: Fn() -> Fal,
Fal: RenderHtml<Rndr>,
Rndr::Element: Clone,
Rndr::Node: Clone,
{
const MIN_LENGTH: usize = if Fal::MIN_LENGTH < AView::MIN_LENGTH {
Fal::MIN_LENGTH
} else {
AView::MIN_LENGTH
};
}*/