initial stage for working nested route rendering
This commit is contained in:
parent
db4c1cb4b3
commit
13cccced06
|
@ -0,0 +1,3 @@
|
||||||
|
[unstable]
|
||||||
|
build-std = ["std", "panic_abort", "core", "alloc"]
|
||||||
|
build-std-features = ["panic_immediate_abort"]
|
|
@ -4,22 +4,24 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
codegen-units = 1
|
opt-level = 'z'
|
||||||
lto = true
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
console_log = "1"
|
console_log = "1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
leptos = { path = "../../leptos", features = ["csr", "tracing"] }
|
leptos = { path = "../../leptos", features = ["csr"] } #, "tracing"] }
|
||||||
routing = { path = "../../routing", features = ["tracing"] }
|
routing = { path = "../../routing" } #, features = ["tracing"] }
|
||||||
#leptos_router = { path = "../../router", features = ["csr"] }
|
#leptos_router = { path = "../../router", features = ["csr", "nightly"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
console_error_panic_hook = "0.1.7"
|
#console_error_panic_hook = "0.1.7"
|
||||||
tracing-subscriber = "0.3.18"
|
#tracing-subscriber = "0.3.18"
|
||||||
tracing-subscriber-wasm = "0.1.0"
|
#tracing-subscriber-wasm = "0.1.0"
|
||||||
tracing = "0.1.40"
|
#tracing = "0.1.40"
|
||||||
#leptos_meta = { path = "../../meta", features = ["csr"] }
|
#leptos_meta = { path = "../../meta", features = ["csr", "nightly"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wasm-bindgen-test = "0.3.0"
|
wasm-bindgen-test = "0.3.0"
|
||||||
|
|
|
@ -12,7 +12,8 @@ use leptos::{
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
use routing::{
|
use routing::{
|
||||||
location::{BrowserUrl, Location},
|
location::{BrowserUrl, Location},
|
||||||
NestedRoute, ParamSegment, RouteData, Router, Routes, StaticSegment,
|
MatchNestedRoutes, NestedRoute, ParamSegment, RouteData, Router, Routes,
|
||||||
|
StaticSegment,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -20,7 +21,39 @@ struct ExampleContext(i32);
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn RouterExample() -> impl IntoView {
|
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
|
// contexts are passed down through the route tree
|
||||||
provide_context(ExampleContext(0));
|
provide_context(ExampleContext(0));
|
||||||
|
@ -28,39 +61,15 @@ pub fn RouterExample() -> impl IntoView {
|
||||||
let router = Router::new(
|
let router = Router::new(
|
||||||
BrowserUrl::new().unwrap(),
|
BrowserUrl::new().unwrap(),
|
||||||
Routes::new((
|
Routes::new((
|
||||||
NestedRoute {
|
NestedRoute::new(StaticSegment("contacts"), ContactList).child((
|
||||||
segments: StaticSegment(""),
|
NestedRoute::new(StaticSegment(""), |_| "Select a contact."),
|
||||||
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
|
// TODO: fix it so empty param doesn't match here, if we reverse the order of
|
||||||
// these two
|
// these two
|
||||||
NestedRoute {
|
NestedRoute::new(ParamSegment("id"), Contact),
|
||||||
segments: ParamSegment("id"),
|
)),
|
||||||
children: (),
|
//NestedRoute::new(StaticSegment(""), ContactList),
|
||||||
data: (),
|
NestedRoute::new(StaticSegment("settings"), Settings),
|
||||||
view: Contact,
|
NestedRoute::new(StaticSegment("about"), About),
|
||||||
},
|
|
||||||
),
|
|
||||||
data: (),
|
|
||||||
view: ContactList,
|
|
||||||
},
|
|
||||||
NestedRoute {
|
|
||||||
segments: StaticSegment("settings"),
|
|
||||||
children: (),
|
|
||||||
data: (),
|
|
||||||
view: Settings,
|
|
||||||
},
|
|
||||||
NestedRoute {
|
|
||||||
segments: StaticSegment("about"),
|
|
||||||
children: (),
|
|
||||||
data: (),
|
|
||||||
view: About,
|
|
||||||
},
|
|
||||||
)),
|
)),
|
||||||
|| "This page could not be found.",
|
|| "This page could not be found.",
|
||||||
);
|
);
|
||||||
|
@ -78,13 +87,13 @@ pub fn RouterExample() -> impl IntoView {
|
||||||
<A href="settings">"Settings"</A>
|
<A href="settings">"Settings"</A>
|
||||||
<A href="redirect-home">"Redirect to Home"</A>
|
<A href="redirect-home">"Redirect to Home"</A>
|
||||||
*/
|
*/
|
||||||
<a href="/">"Contacts"</a>
|
<a href="/contacts">"Contacts"</a>
|
||||||
<a href="about">"About"</a>
|
<a href="/about">"About"</a>
|
||||||
<a href="settings">"Settings"</a>
|
<a href="/settings">"Settings"</a>
|
||||||
<a href="redirect-home">"Redirect to Home"</a>
|
<a href="/redirect-home">"Redirect to Home"</a>
|
||||||
</nav>
|
</nav>
|
||||||
{router}
|
{router}
|
||||||
/*<Router>
|
<Router>
|
||||||
<nav>
|
<nav>
|
||||||
// ordinary <a> elements can be used for client-side navigation
|
// ordinary <a> elements can be used for client-side navigation
|
||||||
// using <A> has two effects:
|
// using <A> has two effects:
|
||||||
|
@ -118,8 +127,20 @@ pub fn RouterExample() -> impl IntoView {
|
||||||
/>
|
/>
|
||||||
</AnimatedRoutes>
|
</AnimatedRoutes>
|
||||||
</main>
|
</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/>");
|
info!("rendering <ContactList/>");
|
||||||
|
|
||||||
// contexts are passed down through the route tree
|
// 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>
|
<pre>"This page is just a placeholder."</pre>
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use router::*;
|
use router::*;
|
||||||
use tracing_subscriber::fmt;
|
//use tracing_subscriber::fmt;
|
||||||
use tracing_subscriber_wasm::MakeConsoleWriter;
|
//use tracing_subscriber_wasm::MakeConsoleWriter;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
fmt()
|
/*fmt()
|
||||||
.with_writer(
|
.with_writer(
|
||||||
MakeConsoleWriter::default()
|
MakeConsoleWriter::default()
|
||||||
.map_trace_level_to(tracing::Level::DEBUG),
|
.map_trace_level_to(tracing::Level::DEBUG),
|
||||||
)
|
)
|
||||||
.without_time()
|
.without_time()
|
||||||
.init();
|
.init();
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();*/
|
||||||
mount_to_body(|| view! { <RouterExample/> })
|
mount_to_body(|| view! { <RouterExample/> })
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,9 +93,6 @@ impl Location for BrowserUrl {
|
||||||
let navigate = {
|
let navigate = {
|
||||||
let url = self.url.clone();
|
let url = self.url.clone();
|
||||||
move |new_url, loc| {
|
move |new_url, loc| {
|
||||||
web_sys::console::log_1(&JsValue::from_str(
|
|
||||||
"updating URL signal",
|
|
||||||
));
|
|
||||||
url.set(new_url);
|
url.set(new_url);
|
||||||
async move {
|
async move {
|
||||||
Self::complete_navigation(&loc);
|
Self::complete_navigation(&loc);
|
||||||
|
|
|
@ -129,6 +129,7 @@ where
|
||||||
|
|
||||||
Box::new(move |ev: Event| {
|
Box::new(move |ev: Event| {
|
||||||
let ev = ev.unchecked_into::<MouseEvent>();
|
let ev = ev.unchecked_into::<MouseEvent>();
|
||||||
|
let origin = window().location().origin()?;
|
||||||
if ev.default_prevented()
|
if ev.default_prevented()
|
||||||
|| ev.button() != 0
|
|| ev.button() != 0
|
||||||
|| ev.meta_key()
|
|| ev.meta_key()
|
||||||
|
@ -168,23 +169,18 @@ where
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let base = window()
|
let url = parse_with_base(href.as_str(), &origin).unwrap();
|
||||||
.location()
|
|
||||||
.origin()
|
|
||||||
.map(Cow::Owned)
|
|
||||||
.unwrap_or(Cow::Borrowed(BASE));
|
|
||||||
let url = parse_with_base(href.as_str(), &base).unwrap();
|
|
||||||
let path_name = unescape(&url.path);
|
let path_name = unescape(&url.path);
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
|
|
||||||
// let browser handle this event if it leaves our domain
|
// let browser handle this event if it leaves our domain
|
||||||
// or our base path
|
// or our base path
|
||||||
if url.origin != window().location().origin().unwrap_or_default()
|
if url.origin != origin
|
||||||
|| (!router_base.is_empty()
|
|| (!router_base.is_empty()
|
||||||
&& !path_name.is_empty()
|
&& !path_name.is_empty()
|
||||||
&& !path_name
|
// NOTE: the two `to_lowercase()` calls here added a total of about 14kb to
|
||||||
.to_lowercase()
|
// release binary size, for limited gain
|
||||||
.starts_with(&router_base.to_lowercase()))
|
&& !path_name.starts_with(&*router_base))
|
||||||
{
|
{
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -203,8 +199,6 @@ where
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ev.prevent_default();
|
|
||||||
|
|
||||||
let replace = Reflect::get(&a, &JsValue::from_str("replace"))
|
let replace = Reflect::get(&a, &JsValue::from_str("replace"))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|value| value.as_bool())
|
.and_then(|value| value.as_bool())
|
||||||
|
|
|
@ -4,6 +4,7 @@ use tachys::{renderer::Renderer, view::Render};
|
||||||
|
|
||||||
pub trait ChooseView<R>
|
pub trait ChooseView<R>
|
||||||
where
|
where
|
||||||
|
Self: 'static,
|
||||||
R: Renderer,
|
R: Renderer,
|
||||||
{
|
{
|
||||||
type Output: Render<R>;
|
type Output: Render<R>;
|
||||||
|
@ -13,7 +14,7 @@ where
|
||||||
|
|
||||||
impl<F, View, R> ChooseView<R> for F
|
impl<F, View, R> ChooseView<R> for F
|
||||||
where
|
where
|
||||||
F: Fn(RouteData<R>) -> View,
|
F: Fn(RouteData<R>) -> View + 'static,
|
||||||
View: Render<R>,
|
View: Render<R>,
|
||||||
R: Renderer,
|
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);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use super::{PartialPathMatch, PathSegment};
|
use super::{PartialPathMatch, PathSegment};
|
||||||
|
use std::borrow::Cow;
|
||||||
mod param_segments;
|
mod param_segments;
|
||||||
mod static_segment;
|
mod static_segment;
|
||||||
mod tuples;
|
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"
|
/// 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).
|
/// matching that sees a tuple as alternatives to one another, see [`RouteChild`](super::RouteChild).
|
||||||
pub trait PossibleRouteMatch {
|
pub trait PossibleRouteMatch {
|
||||||
type ParamsIter<'a>: IntoIterator<Item = (&'a str, &'a str)>;
|
type ParamsIter: IntoIterator<Item = (Cow<'static, str>, String)>;
|
||||||
|
|
||||||
fn test<'a>(
|
fn test<'a>(
|
||||||
&self,
|
&self,
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
) -> Option<PartialPathMatch<'a, Self::ParamsIter<'a>>>;
|
) -> Option<PartialPathMatch<'a, Self::ParamsIter>>;
|
||||||
|
|
||||||
fn generate_path(&self, path: &mut Vec<PathSegment>);
|
fn generate_path(&self, path: &mut Vec<PathSegment>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
use super::{PartialPathMatch, PathSegment, PossibleRouteMatch};
|
use super::{PartialPathMatch, PathSegment, PossibleRouteMatch};
|
||||||
use core::iter;
|
use core::iter;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct ParamSegment(pub &'static str);
|
pub struct ParamSegment(pub &'static str);
|
||||||
|
|
||||||
impl PossibleRouteMatch for ParamSegment {
|
impl PossibleRouteMatch for ParamSegment {
|
||||||
type ParamsIter<'a> = iter::Once<(&'a str, &'a str)>;
|
type ParamsIter = iter::Once<(Cow<'static, str>, String)>;
|
||||||
|
|
||||||
fn test<'a>(
|
fn test<'a>(
|
||||||
&self,
|
&self,
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
) -> Option<PartialPathMatch<'a, Self::ParamsIter<'a>>> {
|
) -> Option<PartialPathMatch<'a, Self::ParamsIter>> {
|
||||||
let mut matched_len = 0;
|
let mut matched_len = 0;
|
||||||
let mut param_offset = 0;
|
let mut param_offset = 0;
|
||||||
let mut param_len = 0;
|
let mut param_len = 0;
|
||||||
|
@ -34,8 +35,10 @@ impl PossibleRouteMatch for ParamSegment {
|
||||||
}
|
}
|
||||||
|
|
||||||
let (matched, remaining) = path.split_at(matched_len);
|
let (matched, remaining) = path.split_at(matched_len);
|
||||||
let param_value =
|
let param_value = iter::once((
|
||||||
iter::once((self.0, &path[param_offset..param_len + param_offset]));
|
Cow::Borrowed(self.0),
|
||||||
|
path[param_offset..param_len + param_offset].to_string(),
|
||||||
|
));
|
||||||
Some(PartialPathMatch::new(remaining, param_value, matched))
|
Some(PartialPathMatch::new(remaining, param_value, matched))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,12 +51,12 @@ impl PossibleRouteMatch for ParamSegment {
|
||||||
pub struct WildcardSegment(pub &'static str);
|
pub struct WildcardSegment(pub &'static str);
|
||||||
|
|
||||||
impl PossibleRouteMatch for WildcardSegment {
|
impl PossibleRouteMatch for WildcardSegment {
|
||||||
type ParamsIter<'a> = iter::Once<(&'a str, &'a str)>;
|
type ParamsIter = iter::Once<(Cow<'static, str>, String)>;
|
||||||
|
|
||||||
fn test<'a>(
|
fn test<'a>(
|
||||||
&self,
|
&self,
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
) -> Option<PartialPathMatch<'a, Self::ParamsIter<'a>>> {
|
) -> Option<PartialPathMatch<'a, Self::ParamsIter>> {
|
||||||
let mut matched_len = 0;
|
let mut matched_len = 0;
|
||||||
let mut param_offset = 0;
|
let mut param_offset = 0;
|
||||||
let mut param_len = 0;
|
let mut param_len = 0;
|
||||||
|
@ -70,8 +73,10 @@ impl PossibleRouteMatch for WildcardSegment {
|
||||||
}
|
}
|
||||||
|
|
||||||
let (matched, remaining) = path.split_at(matched_len);
|
let (matched, remaining) = path.split_at(matched_len);
|
||||||
let param_value =
|
let param_value = iter::once((
|
||||||
iter::once((self.0, &path[param_offset..param_len + param_offset]));
|
Cow::Borrowed(self.0),
|
||||||
|
path[param_offset..param_len + param_offset].to_string(),
|
||||||
|
));
|
||||||
Some(PartialPathMatch::new(remaining, param_value, matched))
|
Some(PartialPathMatch::new(remaining, param_value, matched))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
use super::{PartialPathMatch, PathSegment, PossibleRouteMatch};
|
use super::{PartialPathMatch, PathSegment, PossibleRouteMatch};
|
||||||
use core::iter;
|
use core::iter;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
impl PossibleRouteMatch for () {
|
impl PossibleRouteMatch for () {
|
||||||
type ParamsIter<'a> = iter::Empty<(&'a str, &'a str)>;
|
type ParamsIter = iter::Empty<(Cow<'static, str>, String)>;
|
||||||
|
|
||||||
fn test<'a>(
|
fn test<'a>(
|
||||||
&self,
|
&self,
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
) -> Option<PartialPathMatch<'a, Self::ParamsIter<'a>>> {
|
) -> Option<PartialPathMatch<'a, Self::ParamsIter>> {
|
||||||
Some(PartialPathMatch::new(path, iter::empty(), ""))
|
Some(PartialPathMatch::new(path, iter::empty(), ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,12 +19,12 @@ impl PossibleRouteMatch for () {
|
||||||
pub struct StaticSegment(pub &'static str);
|
pub struct StaticSegment(pub &'static str);
|
||||||
|
|
||||||
impl PossibleRouteMatch for StaticSegment {
|
impl PossibleRouteMatch for StaticSegment {
|
||||||
type ParamsIter<'a> = iter::Empty<(&'a str, &'a str)>;
|
type ParamsIter = iter::Empty<(Cow<'static, str>, String)>;
|
||||||
|
|
||||||
fn test<'a>(
|
fn test<'a>(
|
||||||
&self,
|
&self,
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
) -> Option<PartialPathMatch<'a, Self::ParamsIter<'a>>> {
|
) -> Option<PartialPathMatch<'a, Self::ParamsIter>> {
|
||||||
let mut matched_len = 0;
|
let mut matched_len = 0;
|
||||||
let mut test = path.chars().peekable();
|
let mut test = path.chars().peekable();
|
||||||
let mut this = self.0.chars();
|
let mut this = self.0.chars();
|
||||||
|
|
|
@ -5,14 +5,14 @@ macro_rules! chain_types {
|
||||||
($first:ty, $second:ty, ) => {
|
($first:ty, $second:ty, ) => {
|
||||||
Chain<
|
Chain<
|
||||||
$first,
|
$first,
|
||||||
<<$second as PossibleRouteMatch>::ParamsIter<'a> as IntoIterator>::IntoIter
|
<<$second as PossibleRouteMatch>::ParamsIter as IntoIterator>::IntoIter
|
||||||
>
|
>
|
||||||
};
|
};
|
||||||
($first:ty, $second:ty, $($rest:ty,)+) => {
|
($first:ty, $second:ty, $($rest:ty,)+) => {
|
||||||
chain_types!(
|
chain_types!(
|
||||||
Chain<
|
Chain<
|
||||||
$first,
|
$first,
|
||||||
<<$second as PossibleRouteMatch>::ParamsIter<'a> as IntoIterator>::IntoIter,
|
<<$second as PossibleRouteMatch>::ParamsIter as IntoIterator>::IntoIter,
|
||||||
>,
|
>,
|
||||||
$($rest,)+
|
$($rest,)+
|
||||||
)
|
)
|
||||||
|
@ -27,9 +27,9 @@ macro_rules! tuples {
|
||||||
$first: PossibleRouteMatch,
|
$first: PossibleRouteMatch,
|
||||||
$($ty: 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;
|
let mut matched_len = 0;
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let ($first, $($ty,)*) = &self;
|
let ($first, $($ty,)*) = &self;
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub use nested::*;
|
||||||
use std::{borrow::Cow, marker::PhantomData};
|
use std::{borrow::Cow, marker::PhantomData};
|
||||||
use tachys::{
|
use tachys::{
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
view::{any_view::IntoAny, Render},
|
view::{any_view::IntoAny, Render, RenderHtml},
|
||||||
};
|
};
|
||||||
pub use vertical::*;
|
pub use vertical::*;
|
||||||
|
|
||||||
|
@ -47,10 +47,7 @@ where
|
||||||
Rndr: Renderer + 'static,
|
Rndr: Renderer + 'static,
|
||||||
Children: MatchNestedRoutes<Rndr>,
|
Children: MatchNestedRoutes<Rndr>,
|
||||||
{
|
{
|
||||||
pub fn match_route<'a>(
|
pub fn match_route(&self, path: &str) -> Option<Children::Match> {
|
||||||
&'a self,
|
|
||||||
path: &'a str,
|
|
||||||
) -> Option<Children::Match<'a>> {
|
|
||||||
let path = match &self.base {
|
let path = match &self.base {
|
||||||
None => path,
|
None => path,
|
||||||
Some(base) => {
|
Some(base) => {
|
||||||
|
@ -89,41 +86,40 @@ where
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct RouteMatchId(pub(crate) u8);
|
pub struct RouteMatchId(pub(crate) u8);
|
||||||
|
|
||||||
pub trait MatchInterface<'a, R>
|
pub trait MatchInterface<R>
|
||||||
where
|
where
|
||||||
R: Renderer,
|
R: Renderer + 'static,
|
||||||
{
|
{
|
||||||
type Params: IntoIterator<Item = (&'a str, &'a str)>;
|
type Child: MatchInterface<R> + MatchParams + 'static;
|
||||||
type Child: MatchInterface<'a, R>;
|
type View: Render<R> + RenderHtml<R> + 'static;
|
||||||
type View: Render<R>;
|
|
||||||
|
|
||||||
fn as_id(&self) -> RouteMatchId;
|
fn as_id(&self) -> RouteMatchId;
|
||||||
|
|
||||||
fn as_matched(&self) -> &str;
|
fn as_matched(&self) -> &str;
|
||||||
|
|
||||||
fn to_params(&self) -> Self::Params;
|
|
||||||
|
|
||||||
fn into_view_and_child(
|
fn into_view_and_child(
|
||||||
self,
|
self,
|
||||||
) -> (
|
) -> (impl ChooseView<R, Output = Self::View>, Option<Self::Child>);
|
||||||
impl ChooseView<R, Output = Self::View> + 'a,
|
}
|
||||||
Option<Self::Child>,
|
|
||||||
);
|
pub trait MatchParams {
|
||||||
|
type Params: IntoIterator<Item = (Cow<'static, str>, String)>;
|
||||||
|
|
||||||
|
fn to_params(&self) -> Self::Params;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MatchNestedRoutes<R>
|
pub trait MatchNestedRoutes<R>
|
||||||
where
|
where
|
||||||
R: Renderer,
|
R: Renderer + 'static,
|
||||||
{
|
{
|
||||||
type Data;
|
type Data;
|
||||||
type Match<'a>: MatchInterface<'a, R>
|
type View;
|
||||||
where
|
type Match: MatchInterface<R> + MatchParams;
|
||||||
Self: 'a;
|
|
||||||
|
|
||||||
fn match_nested<'a>(
|
fn match_nested<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
) -> (Option<(RouteMatchId, Self::Match<'a>)>, &str);
|
) -> (Option<(RouteMatchId, Self::Match)>, &str);
|
||||||
|
|
||||||
fn generate_routes(
|
fn generate_routes(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -2,10 +2,13 @@ use super::{
|
||||||
MatchInterface, MatchNestedRoutes, PartialPathMatch, PathSegment,
|
MatchInterface, MatchNestedRoutes, PartialPathMatch, PathSegment,
|
||||||
PossibleRouteMatch, RouteMatchId,
|
PossibleRouteMatch, RouteMatchId,
|
||||||
};
|
};
|
||||||
use crate::{ChooseView, RouteData};
|
use crate::{ChooseView, MatchParams, RouteData};
|
||||||
use core::{fmt, iter};
|
use core::{fmt, iter};
|
||||||
use std::marker::PhantomData;
|
use std::{borrow::Cow, marker::PhantomData};
|
||||||
use tachys::{renderer::Renderer, view::Render};
|
use tachys::{
|
||||||
|
renderer::Renderer,
|
||||||
|
view::{Render, RenderHtml},
|
||||||
|
};
|
||||||
|
|
||||||
mod tuples;
|
mod tuples;
|
||||||
|
|
||||||
|
@ -18,20 +21,58 @@ pub struct NestedRoute<Segments, Children, Data, ViewFn, R> {
|
||||||
pub rndr: PhantomData<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)]
|
#[derive(PartialEq, Eq)]
|
||||||
pub struct NestedMatch<'a, ParamsIter, Child, ViewFn> {
|
pub struct NestedMatch<ParamsIter, Child, ViewFn> {
|
||||||
id: RouteMatchId,
|
id: RouteMatchId,
|
||||||
/// The portion of the full path matched only by this nested route.
|
/// 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.
|
/// The map of params matched only by this nested route.
|
||||||
params: ParamsIter,
|
params: ParamsIter,
|
||||||
/// The nested route.
|
/// The nested route.
|
||||||
child: Child,
|
child: Child,
|
||||||
view_fn: &'a ViewFn,
|
view_fn: ViewFn,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, ParamsIter, Child, ViewFn> fmt::Debug
|
impl<ParamsIter, Child, ViewFn> fmt::Debug
|
||||||
for NestedMatch<'a, ParamsIter, Child, ViewFn>
|
for NestedMatch<ParamsIter, Child, ViewFn>
|
||||||
where
|
where
|
||||||
ParamsIter: fmt::Debug,
|
ParamsIter: fmt::Debug,
|
||||||
Child: fmt::Debug,
|
Child: fmt::Debug,
|
||||||
|
@ -45,16 +86,27 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, ParamsIter, Child, ViewFn, Rndr> MatchInterface<'a, Rndr>
|
impl<ParamsIter, Child, ViewFn> MatchParams
|
||||||
for NestedMatch<'a, ParamsIter, Child, ViewFn>
|
for NestedMatch<ParamsIter, Child, ViewFn>
|
||||||
where
|
where
|
||||||
Rndr: Renderer + 'static,
|
ParamsIter: IntoIterator<Item = (Cow<'static, str>, String)> + Clone,
|
||||||
ParamsIter: IntoIterator<Item = (&'a str, &'a str)> + Clone,
|
|
||||||
Child: MatchInterface<'a, Rndr>,
|
|
||||||
ViewFn: Fn(RouteData<Rndr>),
|
|
||||||
ViewFn::Output: Render<Rndr>,
|
|
||||||
{
|
{
|
||||||
type Params = ParamsIter;
|
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 Child = Child;
|
||||||
type View = ViewFn::Output;
|
type View = ViewFn::Output;
|
||||||
|
|
||||||
|
@ -63,50 +115,44 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_matched(&self) -> &str {
|
fn as_matched(&self) -> &str {
|
||||||
self.matched
|
&self.matched
|
||||||
}
|
|
||||||
|
|
||||||
fn to_params(&self) -> Self::Params {
|
|
||||||
self.params.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_view_and_child(
|
fn into_view_and_child(
|
||||||
self,
|
self,
|
||||||
) -> (
|
) -> (
|
||||||
impl ChooseView<Rndr, Output = Self::View> + 'a,
|
impl ChooseView<Rndr, Output = Self::View>,
|
||||||
Option<Self::Child>,
|
Option<Self::Child>,
|
||||||
) {
|
) {
|
||||||
(self.view_fn, Some(self.child))
|
(self.view_fn, Some(self.child))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, ParamsIter, Child, ViewFn> NestedMatch<'a, ParamsIter, Child, ViewFn> {
|
impl<Segments, Children, Data, ViewFn, View, Rndr> MatchNestedRoutes<Rndr>
|
||||||
pub fn matched(&self) -> &'a str {
|
|
||||||
self.matched
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Segments, Children, Data, ViewFn, Rndr> MatchNestedRoutes<Rndr>
|
|
||||||
for NestedRoute<Segments, Children, Data, ViewFn, Rndr>
|
for NestedRoute<Segments, Children, Data, ViewFn, Rndr>
|
||||||
where
|
where
|
||||||
Rndr: Renderer + 'static,
|
Rndr: Renderer + 'static,
|
||||||
Segments: PossibleRouteMatch,
|
Segments: PossibleRouteMatch,
|
||||||
|
<<Segments as PossibleRouteMatch>::ParamsIter as IntoIterator>::IntoIter: Clone,
|
||||||
Children: MatchNestedRoutes<Rndr>,
|
Children: MatchNestedRoutes<Rndr>,
|
||||||
for<'a> <Segments::ParamsIter<'a> as IntoIterator>::IntoIter: Clone,
|
<<<Children as MatchNestedRoutes<Rndr>>::Match as MatchParams>::Params as IntoIterator>::IntoIter: Clone,
|
||||||
for <'a> <<Children::Match<'a> as MatchInterface<'a, Rndr>>::Params as IntoIterator>::IntoIter:
|
Children::Match: MatchParams,
|
||||||
Clone,
|
Children: 'static,
|
||||||
ViewFn: Fn(RouteData<Rndr>),
|
<Children::Match as MatchParams>::Params: Clone,
|
||||||
|
ViewFn: Fn(RouteData<Rndr>) -> View + Clone + 'static,
|
||||||
|
View: Render<Rndr> + RenderHtml<Rndr> + 'static,
|
||||||
{
|
{
|
||||||
type Data = Data;
|
type Data = Data;
|
||||||
type Match<'a> = NestedMatch<'a, iter::Chain<
|
type View = View;
|
||||||
<Segments::ParamsIter<'a> as IntoIterator>::IntoIter,
|
type Match = NestedMatch<iter::Chain<
|
||||||
<<Children::Match<'a> as MatchInterface<'a, Rndr>>::Params as IntoIterator>::IntoIter,
|
<Segments::ParamsIter as IntoIterator>::IntoIter,
|
||||||
>, Children::Match<'a>, ViewFn> where <Children as MatchNestedRoutes<Rndr>>::Match<'a>: 'a, ViewFn: 'a, Children: 'a, Segments: 'a, Data: 'a;
|
<<Children::Match as MatchParams>::Params as IntoIterator>::IntoIter,
|
||||||
|
>, Children::Match, ViewFn>;
|
||||||
|
|
||||||
fn match_nested<'a>(
|
fn match_nested<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
) -> (Option<(RouteMatchId, Self::Match<'a>)>, &'a str) {
|
) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {
|
||||||
self.segments
|
self.segments
|
||||||
.test(path)
|
.test(path)
|
||||||
.and_then(
|
.and_then(
|
||||||
|
@ -126,10 +172,10 @@ where
|
||||||
id,
|
id,
|
||||||
NestedMatch {
|
NestedMatch {
|
||||||
id,
|
id,
|
||||||
matched,
|
matched: matched.to_string(),
|
||||||
params: params.chain(inner.to_params()),
|
params: params.chain(inner.to_params()),
|
||||||
child: inner,
|
child: inner,
|
||||||
view_fn: &self.view,
|
view_fn: self.view.clone(),
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
remaining,
|
remaining,
|
||||||
|
|
|
@ -1,14 +1,22 @@
|
||||||
use super::{MatchInterface, MatchNestedRoutes, PathSegment, RouteMatchId};
|
use super::{MatchInterface, MatchNestedRoutes, PathSegment, RouteMatchId};
|
||||||
use crate::ChooseView;
|
use crate::{ChooseView, MatchParams};
|
||||||
use core::iter;
|
use core::iter;
|
||||||
use either_of::*;
|
use either_of::*;
|
||||||
|
use std::borrow::Cow;
|
||||||
use tachys::renderer::Renderer;
|
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
|
where
|
||||||
Rndr: Renderer,
|
Rndr: Renderer + 'static,
|
||||||
{
|
{
|
||||||
type Params = iter::Empty<(&'a str, &'a str)>;
|
|
||||||
type Child = ();
|
type Child = ();
|
||||||
type View = ();
|
type View = ();
|
||||||
|
|
||||||
|
@ -20,14 +28,10 @@ where
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_params(&self) -> Self::Params {
|
|
||||||
iter::empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_view_and_child(
|
fn into_view_and_child(
|
||||||
self,
|
self,
|
||||||
) -> (
|
) -> (
|
||||||
impl ChooseView<Rndr, Output = Self::View> + 'a,
|
impl ChooseView<Rndr, Output = Self::View>,
|
||||||
Option<Self::Child>,
|
Option<Self::Child>,
|
||||||
) {
|
) {
|
||||||
((), None)
|
((), None)
|
||||||
|
@ -36,15 +40,16 @@ where
|
||||||
|
|
||||||
impl<Rndr> MatchNestedRoutes<Rndr> for ()
|
impl<Rndr> MatchNestedRoutes<Rndr> for ()
|
||||||
where
|
where
|
||||||
Rndr: Renderer,
|
Rndr: Renderer + 'static,
|
||||||
{
|
{
|
||||||
type Data = ();
|
type Data = ();
|
||||||
type Match<'a> = ();
|
type View = ();
|
||||||
|
type Match = ();
|
||||||
|
|
||||||
fn match_nested<'a>(
|
fn match_nested<'a>(
|
||||||
&self,
|
&self,
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
) -> (Option<(RouteMatchId, Self::Match<'a>)>, &'a str) {
|
) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {
|
||||||
(Some((RouteMatchId(0), ())), path)
|
(Some((RouteMatchId(0), ())), path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,12 +60,22 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, A, Rndr> MatchInterface<'a, Rndr> for (A,)
|
impl<A> MatchParams for (A,)
|
||||||
where
|
where
|
||||||
A: MatchInterface<'a, Rndr>,
|
A: MatchParams,
|
||||||
Rndr: Renderer,
|
|
||||||
{
|
{
|
||||||
type Params = A::Params;
|
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 Child = A::Child;
|
||||||
type View = A::View;
|
type View = A::View;
|
||||||
|
|
||||||
|
@ -72,14 +87,10 @@ where
|
||||||
self.0.as_matched()
|
self.0.as_matched()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_params(&self) -> Self::Params {
|
|
||||||
self.0.to_params()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_view_and_child(
|
fn into_view_and_child(
|
||||||
self,
|
self,
|
||||||
) -> (
|
) -> (
|
||||||
impl ChooseView<Rndr, Output = Self::View> + 'a,
|
impl ChooseView<Rndr, Output = Self::View>,
|
||||||
Option<Self::Child>,
|
Option<Self::Child>,
|
||||||
) {
|
) {
|
||||||
self.0.into_view_and_child()
|
self.0.into_view_and_child()
|
||||||
|
@ -89,15 +100,16 @@ where
|
||||||
impl<A, Rndr> MatchNestedRoutes<Rndr> for (A,)
|
impl<A, Rndr> MatchNestedRoutes<Rndr> for (A,)
|
||||||
where
|
where
|
||||||
A: MatchNestedRoutes<Rndr>,
|
A: MatchNestedRoutes<Rndr>,
|
||||||
Rndr: Renderer,
|
Rndr: Renderer + 'static,
|
||||||
{
|
{
|
||||||
type Data = A::Data;
|
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>(
|
fn match_nested<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
) -> (Option<(RouteMatchId, Self::Match<'a>)>, &'a str) {
|
) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {
|
||||||
self.0.match_nested(path)
|
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
|
where
|
||||||
Rndr: Renderer,
|
A: MatchParams,
|
||||||
A: MatchInterface<'a, Rndr>,
|
B: MatchParams,
|
||||||
B: MatchInterface<'a, Rndr>,
|
|
||||||
{
|
{
|
||||||
type Params = Either<
|
type Params = Either<
|
||||||
<A::Params as IntoIterator>::IntoIter,
|
<A::Params as IntoIterator>::IntoIter,
|
||||||
<B::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 Child = Either<A::Child, B::Child>;
|
||||||
type View = Either<A::View, B::View>;
|
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(
|
fn into_view_and_child(
|
||||||
self,
|
self,
|
||||||
) -> (
|
) -> (
|
||||||
impl ChooseView<Rndr, Output = Self::View> + 'a,
|
impl ChooseView<Rndr, Output = Self::View>,
|
||||||
Option<Self::Child>,
|
Option<Self::Child>,
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
|
@ -168,12 +187,13 @@ where
|
||||||
Rndr: Renderer + 'static,
|
Rndr: Renderer + 'static,
|
||||||
{
|
{
|
||||||
type Data = (A::Data, B::Data);
|
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>(
|
fn match_nested<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
) -> (Option<(RouteMatchId, Self::Match<'a>)>, &'a str) {
|
) -> (Option<(RouteMatchId, Self::Match)>, &'a str) {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let (A, B) = &self;
|
let (A, B) = &self;
|
||||||
if let (Some((id, matched)), remaining) = A.match_nested(path) {
|
if let (Some((id, matched)), remaining) = A.match_nested(path) {
|
||||||
|
@ -213,15 +233,26 @@ macro_rules! chain_generated {
|
||||||
|
|
||||||
macro_rules! tuples {
|
macro_rules! tuples {
|
||||||
($either:ident => $($ty:ident = $count:expr),*) => {
|
($either:ident => $($ty:ident = $count:expr),*) => {
|
||||||
impl<'a, Rndr, $($ty,)*> MatchInterface<'a, Rndr> for $either <$($ty,)*>
|
impl<'a, $($ty,)*> MatchParams for $either <$($ty,)*>
|
||||||
where
|
where
|
||||||
Rndr: Renderer + 'static,
|
$($ty: MatchParams),*,
|
||||||
$($ty: MatchInterface<'a, Rndr>),*,
|
|
||||||
$($ty::Child: 'a),*,
|
|
||||||
{
|
{
|
||||||
type Params = $either<$(
|
type Params = $either<$(
|
||||||
<$ty::Params as IntoIterator>::IntoIter,
|
<$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 Child = $either<$($ty::Child,)*>;
|
||||||
type View = $either<$($ty::View,)*>;
|
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 {
|
match self {
|
||||||
$($either::$ty(i) => $either::$ty(i.to_params().into_iter()),)*
|
$($either::$ty(i) => {
|
||||||
}
|
let (view, child) = i.into_view_and_child();
|
||||||
}
|
($either::$ty(view), child.map($either::$ty))
|
||||||
|
})*
|
||||||
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()),)*
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Rndr, $($ty),*> MatchNestedRoutes<'a, Rndr> for ($($ty,)*)
|
impl<Rndr, $($ty),*> MatchNestedRoutes<Rndr> for ($($ty,)*)
|
||||||
where
|
where
|
||||||
Rndr: Renderer + 'static,
|
Rndr: Renderer + 'static,
|
||||||
$($ty: MatchNestedRoutes<'a, Rndr>),*,
|
$($ty: MatchNestedRoutes<Rndr>),*,
|
||||||
$($ty::Match: 'a),*,
|
|
||||||
{
|
{
|
||||||
type Data = ($($ty::Data,)*);
|
type Data = ($($ty::Data,)*);
|
||||||
|
type View = $either<$($ty::View,)*>;
|
||||||
type Match = $either<$($ty::Match,)*>;
|
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)]
|
#[allow(non_snake_case)]
|
||||||
|
|
||||||
let ($($ty,)*) = &self;
|
let ($($ty,)*) = &self;
|
||||||
|
@ -290,9 +317,9 @@ macro_rules! tuples {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
tuples!(EitherOf3 => A = 0, B = 1, C = 2);
|
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!(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!(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!(EitherOf7 => A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6);
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
MatchInterface, MatchNestedRoutes, PossibleRouteMatch, RouteMatchId,
|
MatchInterface, MatchNestedRoutes, PossibleRouteMatch, RouteMatchId,
|
||||||
Routes,
|
Routes,
|
||||||
},
|
},
|
||||||
ChooseView, Params,
|
ChooseView, MatchParams, Params,
|
||||||
};
|
};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use either_of::*;
|
use either_of::*;
|
||||||
|
@ -45,6 +45,7 @@ where
|
||||||
Rndr: Renderer,
|
Rndr: Renderer,
|
||||||
FallbackFn: Fn() -> Fallback,
|
FallbackFn: Fn() -> Fallback,
|
||||||
{
|
{
|
||||||
|
#[inline(always)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
location: Loc,
|
location: Loc,
|
||||||
routes: Routes<Children, Rndr>,
|
routes: Routes<Children, Rndr>,
|
||||||
|
@ -58,6 +59,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn new_with_base(
|
pub fn new_with_base(
|
||||||
base: impl Into<Cow<'static, str>>,
|
base: impl Into<Cow<'static, str>>,
|
||||||
location: Loc,
|
location: Loc,
|
||||||
|
@ -99,13 +101,19 @@ where
|
||||||
FallbackFn: Fn() -> Fallback + 'static,
|
FallbackFn: Fn() -> Fallback + 'static,
|
||||||
Fallback: Render<Rndr>,
|
Fallback: Render<Rndr>,
|
||||||
Children: MatchNestedRoutes<Rndr> + 'static,
|
Children: MatchNestedRoutes<Rndr> + 'static,
|
||||||
|
//for<'a> <Children::Match<'a> as MatchInterface<Rndr, View = View>>,
|
||||||
/*View: Render<Rndr> + IntoAny<Rndr> + 'static,
|
/*View: Render<Rndr> + IntoAny<Rndr> + 'static,
|
||||||
View::State: 'static,*/
|
View::State: 'static,*/
|
||||||
Fallback::State: 'static,
|
Fallback::State: 'static,
|
||||||
Rndr: Renderer + 'static,
|
Rndr: Renderer + 'static,
|
||||||
{
|
{
|
||||||
type State =
|
type State = RenderEffect<
|
||||||
RenderEffect<EitherState<(), <Fallback as Render<Rndr>>::State, Rndr>>;
|
EitherState<
|
||||||
|
<NestedRouteView<Children::Match, Rndr> as Render<Rndr>>::State,
|
||||||
|
<Fallback as Render<Rndr>>::State,
|
||||||
|
Rndr,
|
||||||
|
>,
|
||||||
|
>;
|
||||||
type FallibleState = ();
|
type FallibleState = ();
|
||||||
|
|
||||||
fn build(self) -> Self::State {
|
fn build(self) -> Self::State {
|
||||||
|
@ -130,33 +138,32 @@ where
|
||||||
if let Some(new_match) = new_match {
|
if let Some(new_match) = new_match {
|
||||||
match &mut prev.state {
|
match &mut prev.state {
|
||||||
Either::Left(prev) => {
|
Either::Left(prev) => {
|
||||||
|
// TODO!
|
||||||
//nested_rebuild(&outer_owner, prev, new_match);
|
//nested_rebuild(&outer_owner, prev, new_match);
|
||||||
}
|
}
|
||||||
Either::Right(_) => {
|
Either::Right(_) => {
|
||||||
/*Either::<_, Fallback>::Left(NestedRouteView::new(
|
Either::<_, Fallback>::Left(
|
||||||
|
NestedRouteView::create(
|
||||||
&outer_owner,
|
&outer_owner,
|
||||||
new_match,
|
new_match,
|
||||||
))
|
),
|
||||||
.rebuild(&mut prev);*/
|
)
|
||||||
|
.rebuild(&mut prev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/*Either::<NestedRouteView<View, Rndr>, _>::Right((self
|
Either::<NestedRouteView<Children::Match, Rndr>, _>::Right(
|
||||||
.fallback)(
|
(self.fallback)(),
|
||||||
))
|
)
|
||||||
.rebuild(&mut prev);*/
|
.rebuild(&mut prev);
|
||||||
}
|
}
|
||||||
prev
|
prev
|
||||||
} else {
|
} else {
|
||||||
match new_match {
|
match new_match {
|
||||||
Some(matched) =>
|
Some(matched) => Either::Left(NestedRouteView::create(
|
||||||
/*Either::Left(NestedRouteView::new(
|
|
||||||
&outer_owner,
|
&outer_owner,
|
||||||
matched,
|
matched,
|
||||||
))*/
|
)),
|
||||||
{
|
|
||||||
Either::Left(())
|
|
||||||
}
|
|
||||||
_ => Either::Right((self.fallback)()),
|
_ => Either::Right((self.fallback)()),
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
|
@ -178,65 +185,155 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*fn nested_rebuild<NewMatch, R>(
|
pub struct NestedRouteView<Matcher, 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>
|
|
||||||
where
|
where
|
||||||
R: Renderer,
|
Matcher: MatchInterface<R>,
|
||||||
|
R: Renderer + 'static,
|
||||||
{
|
{
|
||||||
id: RouteMatchId,
|
id: RouteMatchId,
|
||||||
owner: Owner,
|
owner: Owner,
|
||||||
params: ArcRwSignal<Params>,
|
params: ArcRwSignal<Params>,
|
||||||
matched: ArcRwSignal<String>,
|
matched: ArcRwSignal<String>,
|
||||||
view: View,
|
view: Matcher::View,
|
||||||
rndr: PhantomData<R>,
|
//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
|
where
|
||||||
R: Renderer + 'static,
|
R: Renderer + 'static,
|
||||||
{
|
{
|
||||||
pub fn new<Matcher>(outer_owner: &Owner, route_match: Matcher) -> Self
|
pub fn new<Matcher>(outer_owner: &Owner, route_match: Matcher) -> Self
|
||||||
where
|
where
|
||||||
Matcher: for<'a> MatchInterface<'a, R, View = View> + 'static,
|
Matcher: for<'a> MatchInterface<R, View = View> + 'static,
|
||||||
for<'a> <Matcher as MatchInterface<'a, R>>::View:
|
for<'a> <Matcher as MatchInterface<R>>::View:
|
||||||
ChooseView<R, Output = 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,
|
View: IntoAny<R> + 'static,
|
||||||
{
|
{
|
||||||
let params = ArcRwSignal::new(
|
let params = ArcRwSignal::new(
|
||||||
|
@ -281,83 +378,104 @@ where
|
||||||
rndr: PhantomData,
|
rndr: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
impl<R, View> Render<R> for NestedRouteView<View, R>
|
/*fn nested_rebuild<NewMatch, R>(
|
||||||
where
|
outer_owner: &Owner,
|
||||||
View: Render<R>,
|
current: &mut NestedRouteState<
|
||||||
R: Renderer,
|
<<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>;
|
// if the new match is a different branch of the nested route tree from the current one, we can
|
||||||
type FallibleState = ();
|
// 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 {
|
// TODO is this the right place to drop the old Owner?
|
||||||
let NestedRouteView {
|
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,
|
id,
|
||||||
owner,
|
owner: outer_owner.child(),
|
||||||
params,
|
params,
|
||||||
matched,
|
matched,
|
||||||
view,
|
view: view.choose(route_data),
|
||||||
rndr,
|
rndr: PhantomData,
|
||||||
} = self;
|
|
||||||
NestedRouteState {
|
|
||||||
id,
|
|
||||||
owner,
|
|
||||||
params,
|
|
||||||
matched,
|
|
||||||
view: view.build(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
fn rebuild(self, state: &mut Self::State) {
|
trait RouteView<R>: for<'a> MatchInterface<R>
|
||||||
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>
|
|
||||||
where
|
where
|
||||||
ViewState: Mountable<R>,
|
R: Renderer + 'static,
|
||||||
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,
|
|
||||||
{
|
{
|
||||||
type RouteViewChild: RouteView<R>;
|
type RouteViewChild: RouteView<R>;
|
||||||
type RouteView: Render<R>;
|
type RouteView: Render<R>;
|
||||||
|
@ -365,23 +483,16 @@ where
|
||||||
fn into_child(self) -> Option<Self::RouteViewChild>;
|
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>
|
for Router<Rndr, Loc, Children, FallbackFn>
|
||||||
where
|
where
|
||||||
Loc: Location,
|
Loc: Location,
|
||||||
FallbackFn: Fn() -> Fallback + 'static,
|
FallbackFn: Fn() -> Fallback + 'static,
|
||||||
Fallback: RenderHtml<Rndr>,
|
Fallback: RenderHtml<Rndr>,
|
||||||
Children: MatchNestedRoutes<Rndr> + 'static,
|
Children: MatchNestedRoutes<Rndr> + 'static,
|
||||||
for<'a> <<Children as MatchNestedRoutes<Rndr>>::Match<'a> as MatchInterface<
|
Children::View: RenderHtml<Rndr>,
|
||||||
'a,
|
/*View: Render<Rndr> + IntoAny<Rndr> + 'static,
|
||||||
Rndr,
|
View::State: 'static,*/
|
||||||
>>::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,
|
|
||||||
Fallback::State: 'static,
|
Fallback::State: 'static,
|
||||||
Rndr: Renderer + 'static,
|
Rndr: Renderer + 'static,
|
||||||
{
|
{
|
||||||
|
@ -408,11 +519,10 @@ where
|
||||||
FallbackFn: Fn() -> Fallback,
|
FallbackFn: Fn() -> Fallback,
|
||||||
Fallback: Render<Rndr>,
|
Fallback: Render<Rndr>,
|
||||||
Children: MatchNestedRoutes<Rndr>,
|
Children: MatchNestedRoutes<Rndr>,
|
||||||
for<'a> <<Children as MatchNestedRoutes<Rndr>>::Match<'a> as MatchInterface<
|
<<Children as MatchNestedRoutes<Rndr>>::Match as MatchInterface<
|
||||||
'a,
|
|
||||||
Rndr,
|
Rndr,
|
||||||
>>::View: ChooseView<Rndr, Output = View>,
|
>>::View: ChooseView<Rndr, Output = View>,
|
||||||
Rndr: Renderer,
|
Rndr: Renderer + 'static,
|
||||||
Router<Rndr, Loc, Children, FallbackFn>: RenderHtml<Rndr>,
|
Router<Rndr, Loc, Children, FallbackFn>: RenderHtml<Rndr>,
|
||||||
{
|
{
|
||||||
type Output<SomeNewAttr: Attribute<Rndr>> = Self;
|
type Output<SomeNewAttr: Attribute<Rndr>> = Self;
|
||||||
|
@ -437,217 +547,3 @@ where
|
||||||
self
|
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
|
|
||||||
};
|
|
||||||
}*/
|
|
||||||
|
|
Loading…
Reference in New Issue