mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
Make necessary changes for stable
support for router
and meta
This commit is contained in:
parent
7f696a9ac4
commit
5c45538e9f
10 changed files with 278 additions and 159 deletions
|
@ -1,5 +1,6 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use wasm_bindgen::convert::FromWasmAbi;
|
use wasm_bindgen::convert::FromWasmAbi;
|
||||||
use wasm_bindgen::{prelude::Closure, JsCast, JsValue, UnwrapThrowExt};
|
use wasm_bindgen::{prelude::Closure, JsCast, JsValue, UnwrapThrowExt};
|
||||||
|
|
||||||
|
@ -254,30 +255,58 @@ pub fn set_interval(
|
||||||
Ok(IntervalHandle(handle))
|
Ok(IntervalHandle(handle))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds an event listener to the target DOM element using implicit event delegation.
|
cfg_if! {
|
||||||
pub fn add_event_listener<E>(
|
if #[cfg(not(feature = "stable"))] {
|
||||||
target: &web_sys::Element,
|
/// Adds an event listener to the target DOM element using implicit event delegation.
|
||||||
event_name: &'static str,
|
pub fn add_event_listener<E>(
|
||||||
cb: impl FnMut(E) + 'static,
|
target: &web_sys::Element,
|
||||||
) where
|
event_name: &'static str,
|
||||||
E: FromWasmAbi + 'static,
|
cb: impl FnMut(E) + 'static,
|
||||||
{
|
) where
|
||||||
let cb = Closure::wrap(Box::new(cb) as Box<dyn FnMut(E)>).into_js_value();
|
E: FromWasmAbi + 'static,
|
||||||
let key = event_delegation::event_delegation_key(event_name);
|
{
|
||||||
_ = js_sys::Reflect::set(target, &JsValue::from_str(&key), &cb);
|
let cb = Closure::wrap(Box::new(cb) as Box<dyn FnMut(E)>).into_js_value();
|
||||||
event_delegation::add_event_listener(event_name);
|
let key = event_delegation::event_delegation_key(event_name);
|
||||||
}
|
_ = js_sys::Reflect::set(target, &JsValue::from_str(&key), &cb);
|
||||||
|
event_delegation::add_event_listener(event_name);
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn add_event_listener_undelegated<E>(
|
pub fn add_event_listener_undelegated<E>(
|
||||||
target: &web_sys::Element,
|
target: &web_sys::Element,
|
||||||
event_name: &'static str,
|
event_name: &'static str,
|
||||||
cb: impl FnMut(E) + 'static,
|
cb: impl FnMut(E) + 'static,
|
||||||
) where
|
) where
|
||||||
E: FromWasmAbi + 'static,
|
E: FromWasmAbi + 'static,
|
||||||
{
|
{
|
||||||
let cb = Closure::wrap(Box::new(cb) as Box<dyn FnMut(E)>).into_js_value();
|
let cb = Closure::wrap(Box::new(cb) as Box<dyn FnMut(E)>).into_js_value();
|
||||||
_ = target.add_event_listener_with_callback(event_name, cb.unchecked_ref());
|
_ = target.add_event_listener_with_callback(event_name, cb.unchecked_ref());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/// Adds an event listener to the target DOM element using implicit event delegation.
|
||||||
|
pub fn add_event_listener(
|
||||||
|
target: &web_sys::Element,
|
||||||
|
event_name: &'static str,
|
||||||
|
cb: impl FnMut(web_sys::Event) + 'static,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
let cb = Closure::wrap(Box::new(cb) as Box<dyn FnMut(web_sys::Event)>).into_js_value();
|
||||||
|
let key = event_delegation::event_delegation_key(event_name);
|
||||||
|
_ = js_sys::Reflect::set(target, &JsValue::from_str(&key), &cb);
|
||||||
|
event_delegation::add_event_listener(event_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn add_event_listener_undelegated(
|
||||||
|
target: &web_sys::Element,
|
||||||
|
event_name: &'static str,
|
||||||
|
cb: impl FnMut(web_sys::Event) + 'static,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
let cb = Closure::wrap(Box::new(cb) as Box<dyn FnMut(web_sys::Event)>).into_js_value();
|
||||||
|
_ = target.add_event_listener_with_callback(event_name, cb.unchecked_ref());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
|
@ -564,14 +564,28 @@ fn attr_to_tokens(
|
||||||
let event_type = event_type.parse::<TokenStream>().expect("couldn't parse event name");
|
let event_type = event_type.parse::<TokenStream>().expect("couldn't parse event name");
|
||||||
|
|
||||||
if mode != Mode::Ssr {
|
if mode != Mode::Ssr {
|
||||||
if NON_BUBBLING_EVENTS.contains(&name.as_str()) {
|
cfg_if::cfg_if! {
|
||||||
expressions.push(quote_spanned! {
|
if #[cfg(feature = "stable")] {
|
||||||
span => ::leptos::add_event_listener_undelegated::<web_sys::#event_type>(#el_id.unchecked_ref(), #name, #handler);
|
if NON_BUBBLING_EVENTS.contains(&name.as_str()) {
|
||||||
});
|
expressions.push(quote_spanned! {
|
||||||
} else {
|
span => ::leptos::add_event_listener_undelegated(#el_id.unchecked_ref(), #name, #handler);
|
||||||
expressions.push(quote_spanned! {
|
});
|
||||||
span => ::leptos::add_event_listener::<web_sys::#event_type>(#el_id.unchecked_ref(), #name, #handler);
|
} else {
|
||||||
});
|
expressions.push(quote_spanned! {
|
||||||
|
span => ::leptos::add_event_listener(#el_id.unchecked_ref(), #name, #handler);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if NON_BUBBLING_EVENTS.contains(&name.as_str()) {
|
||||||
|
expressions.push(quote_spanned! {
|
||||||
|
span => ::leptos::add_event_listener_undelegated::<web_sys::#event_type>(#el_id.unchecked_ref(), #name, #handler);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
expressions.push(quote_spanned! {
|
||||||
|
span => ::leptos::add_event_listener::<web_sys::#event_type>(#el_id.unchecked_ref(), #name, #handler);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "leptos_meta"
|
name = "leptos_meta"
|
||||||
version = "0.0.4"
|
version = "0.0.5"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Greg Johnston"]
|
authors = ["Greg Johnston"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "leptos_router"
|
name = "leptos_router"
|
||||||
version = "0.0.5"
|
version = "0.0.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Greg Johnston"]
|
authors = ["Greg Johnston"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
@ -58,7 +58,8 @@ default = ["csr"]
|
||||||
csr = ["leptos/csr"]
|
csr = ["leptos/csr"]
|
||||||
hydrate = ["leptos/hydrate"]
|
hydrate = ["leptos/hydrate"]
|
||||||
ssr = ["leptos/ssr", "dep:url", "dep:regex"]
|
ssr = ["leptos/ssr", "dep:url", "dep:regex"]
|
||||||
|
stable = ["leptos/stable"]
|
||||||
|
|
||||||
[package.metadata.cargo-all-features]
|
[package.metadata.cargo-all-features]
|
||||||
# No need to test optional dependencies as they are enabled by the ssr feature
|
# No need to test optional dependencies as they are enabled by the ssr feature
|
||||||
denylist = ["url", "regex"]
|
denylist = ["url", "regex", "stable"]
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::{use_navigate, use_resolved_path, TextProp};
|
use crate::{use_navigate, use_resolved_path, TextProp};
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use std::{error::Error, rc::Rc};
|
use std::{error::Error, rc::Rc};
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
|
@ -131,15 +132,37 @@ where
|
||||||
|
|
||||||
let children = children();
|
let children = children();
|
||||||
|
|
||||||
view! { cx,
|
cfg_if! {
|
||||||
<form
|
if #[cfg(feature = "stable")] {
|
||||||
method=method
|
let on_submit = move |ev: web_sys::Event| on_submit(ev.unchecked_into());
|
||||||
action=action
|
}
|
||||||
enctype=enctype
|
};
|
||||||
on:submit=on_submit
|
|
||||||
>
|
cfg_if! {
|
||||||
{children}
|
if #[cfg(not(feature = "stable"))] {
|
||||||
</form>
|
view! { cx,
|
||||||
|
<form
|
||||||
|
method=method
|
||||||
|
action=action
|
||||||
|
enctype=enctype
|
||||||
|
on:submit=on_submit
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
view! { cx,
|
||||||
|
<form
|
||||||
|
method=method
|
||||||
|
action=move || action.get()
|
||||||
|
enctype=enctype
|
||||||
|
on:submit=on_submit
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,6 +305,12 @@ where
|
||||||
|
|
||||||
let children = (props.children)();
|
let children = (props.children)();
|
||||||
|
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(feature = "stable")] {
|
||||||
|
let on_submit = move |ev: web_sys::Event| on_submit(ev.unchecked_into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
view! { cx,
|
view! { cx,
|
||||||
<form
|
<form
|
||||||
method="POST"
|
method="POST"
|
||||||
|
|
|
@ -68,7 +68,6 @@ impl std::fmt::Debug for RouterContextInner {
|
||||||
f.debug_struct("RouterContextInner")
|
f.debug_struct("RouterContextInner")
|
||||||
.field("location", &self.location)
|
.field("location", &self.location)
|
||||||
.field("base", &self.base)
|
.field("base", &self.base)
|
||||||
.field("history", &std::any::type_name_of_val(&self.history))
|
|
||||||
.field("cx", &self.cx)
|
.field("cx", &self.cx)
|
||||||
.field("reference", &self.reference)
|
.field("reference", &self.reference)
|
||||||
.field("set_reference", &self.set_reference)
|
.field("set_reference", &self.set_reference)
|
||||||
|
@ -103,14 +102,16 @@ impl RouterContext {
|
||||||
let base = base.unwrap_or_default();
|
let base = base.unwrap_or_default();
|
||||||
let base_path = resolve_path("", base, None);
|
let base_path = resolve_path("", base, None);
|
||||||
|
|
||||||
if let Some(base_path) = &base_path && source.with(|s| s.value.is_empty()) {
|
if let Some(base_path) = &base_path {
|
||||||
history.navigate(&LocationChange {
|
if source.with(|s| s.value.is_empty()) {
|
||||||
value: base_path.to_string(),
|
history.navigate(&LocationChange {
|
||||||
replace: true,
|
value: base_path.to_string(),
|
||||||
scroll: false,
|
replace: true,
|
||||||
state: State(None)
|
scroll: false,
|
||||||
});
|
state: State(None),
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// the current URL
|
// the current URL
|
||||||
let (reference, set_reference) = create_signal(cx, source.with(|s| s.value.clone()));
|
let (reference, set_reference) = create_signal(cx, source.with(|s| s.value.clone()));
|
||||||
|
@ -136,9 +137,9 @@ impl RouterContext {
|
||||||
// 3) update the state
|
// 3) update the state
|
||||||
// this will trigger the new route match below
|
// this will trigger the new route match below
|
||||||
create_render_effect(cx, move |_| {
|
create_render_effect(cx, move |_| {
|
||||||
let LocationChange { value, state, .. } = source();
|
let LocationChange { value, state, .. } = source.get();
|
||||||
cx.untrack(move || {
|
cx.untrack(move || {
|
||||||
if value != reference() {
|
if value != reference.get() {
|
||||||
set_reference.update(move |r| *r = value);
|
set_reference.update(move |r| *r = value);
|
||||||
set_state.update(move |s| *s = state);
|
set_state.update(move |s| *s = state);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,20 @@
|
||||||
use std::{cmp::Reverse, rc::Rc, cell::{RefCell, Cell}, ops::IndexMut};
|
use std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
cmp::Reverse,
|
||||||
|
ops::IndexMut,
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
use crate::{matching::{expand_optionals, join_paths, Branch, Matcher, RouteDefinition, get_route_matches, RouteMatch}, RouterContext, RouteContext};
|
use crate::{
|
||||||
|
matching::{
|
||||||
|
expand_optionals, get_route_matches, join_paths, Branch, Matcher, RouteDefinition,
|
||||||
|
RouteMatch,
|
||||||
|
},
|
||||||
|
RouteContext, RouterContext,
|
||||||
|
};
|
||||||
|
|
||||||
/// Props for the [Routes] component, which contains route definitions and manages routing.
|
/// Props for the [Routes] component, which contains route definitions and manages routing.
|
||||||
#[derive(TypedBuilder)]
|
#[derive(TypedBuilder)]
|
||||||
|
@ -34,9 +45,7 @@ pub fn Routes(cx: Scope, props: RoutesProps) -> impl IntoChild {
|
||||||
// whenever path changes, update matches
|
// whenever path changes, update matches
|
||||||
let matches = create_memo(cx, {
|
let matches = create_memo(cx, {
|
||||||
let router = router.clone();
|
let router = router.clone();
|
||||||
move |_| {
|
move |_| get_route_matches(branches.clone(), router.pathname().get())
|
||||||
get_route_matches(branches.clone(), router.pathname().get())
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Rebuild the list of nested routes conservatively, and show the root route here
|
// Rebuild the list of nested routes conservatively, and show the root route here
|
||||||
|
@ -68,61 +77,66 @@ pub fn Routes(cx: Scope, props: RoutesProps) -> impl IntoChild {
|
||||||
let prev_match = prev_matches.and_then(|p| p.get(i));
|
let prev_match = prev_matches.and_then(|p| p.get(i));
|
||||||
let next_match = next_matches.get(i).unwrap();
|
let next_match = next_matches.get(i).unwrap();
|
||||||
|
|
||||||
if let Some(prev) = prev_routes && let Some(prev_match) = prev_match && next_match.route.key == prev_match.route.key {
|
match (prev_routes, prev_match) {
|
||||||
let prev_one = { prev.borrow()[i].clone() };
|
(Some(prev), Some(prev_match))
|
||||||
if i >= next.borrow().len() {
|
if next_match.route.key == prev_match.route.key =>
|
||||||
next.borrow_mut().push(prev_one);
|
{
|
||||||
} else {
|
let prev_one = { prev.borrow()[i].clone() };
|
||||||
*(next.borrow_mut().index_mut(i)) = prev_one;
|
if i >= next.borrow().len() {
|
||||||
}
|
next.borrow_mut().push(prev_one);
|
||||||
} else {
|
} else {
|
||||||
equal = false;
|
*(next.borrow_mut().index_mut(i)) = prev_one;
|
||||||
if i == 0 {
|
}
|
||||||
root_equal.set(false);
|
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
|
equal = false;
|
||||||
|
if i == 0 {
|
||||||
|
root_equal.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
let disposer = cx.child_scope({
|
let disposer = cx.child_scope({
|
||||||
let next = next.clone();
|
|
||||||
let router = Rc::clone(&router.inner);
|
|
||||||
move |cx| {
|
|
||||||
let next = next.clone();
|
let next = next.clone();
|
||||||
let next_ctx = RouteContext::new(
|
let router = Rc::clone(&router.inner);
|
||||||
cx,
|
move |cx| {
|
||||||
&RouterContext { inner: router },
|
let next = next.clone();
|
||||||
{
|
let next_ctx = RouteContext::new(
|
||||||
let next = next.clone();
|
cx,
|
||||||
move || {
|
&RouterContext { inner: router },
|
||||||
if let Some(route_states) = use_context::<Memo<RouterState>>(cx) {
|
{
|
||||||
route_states.with(|route_states| {
|
let next = next.clone();
|
||||||
let routes = route_states.routes.borrow();
|
move || {
|
||||||
routes.get(i + 1).cloned()
|
if let Some(route_states) =
|
||||||
})
|
use_context::<Memo<RouterState>>(cx)
|
||||||
} else {
|
{
|
||||||
next.borrow().get(i + 1).cloned()
|
route_states.with(|route_states| {
|
||||||
|
let routes = route_states.routes.borrow();
|
||||||
|
routes.get(i + 1).cloned()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
next.borrow().get(i + 1).cloned()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
move || matches.with(|m| m.get(i).cloned()),
|
||||||
move || {
|
);
|
||||||
matches.with(|m| m.get(i).cloned())
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(next_ctx) = next_ctx {
|
if let Some(next_ctx) = next_ctx {
|
||||||
if next.borrow().len() > i + 1 {
|
if next.borrow().len() > i + 1 {
|
||||||
next.borrow_mut()[i] = next_ctx;
|
next.borrow_mut()[i] = next_ctx;
|
||||||
} else {
|
} else {
|
||||||
next.borrow_mut().push(next_ctx);
|
next.borrow_mut().push(next_ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
if disposers.borrow().len() > i + 1 {
|
if disposers.borrow().len() > i + 1 {
|
||||||
let mut disposers = disposers.borrow_mut();
|
let mut disposers = disposers.borrow_mut();
|
||||||
let old_route_disposer = std::mem::replace(&mut disposers[i], disposer);
|
let old_route_disposer = std::mem::replace(&mut disposers[i], disposer);
|
||||||
old_route_disposer.dispose();
|
old_route_disposer.dispose();
|
||||||
} else {
|
} else {
|
||||||
disposers.borrow_mut().push(disposer);
|
disposers.borrow_mut().push(disposer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,25 +148,34 @@ pub fn Routes(cx: Scope, props: RoutesProps) -> impl IntoChild {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(prev) = &prev && equal {
|
if let Some(prev) = &prev {
|
||||||
RouterState {
|
if equal {
|
||||||
matches: next_matches.to_vec(),
|
RouterState {
|
||||||
routes: prev_routes.cloned().unwrap_or_default(),
|
matches: next_matches.to_vec(),
|
||||||
root: prev.root.clone(),
|
routes: prev_routes.cloned().unwrap_or_default(),
|
||||||
|
root: prev.root.clone(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let root = next.borrow().get(0).cloned();
|
||||||
|
RouterState {
|
||||||
|
matches: next_matches.to_vec(),
|
||||||
|
routes: Rc::new(RefCell::new(next.borrow().to_vec())),
|
||||||
|
root,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let root = next.borrow().get(0).cloned();
|
let root = next.borrow().get(0).cloned();
|
||||||
RouterState {
|
RouterState {
|
||||||
matches: next_matches.to_vec(),
|
matches: next_matches.to_vec(),
|
||||||
routes: Rc::new(RefCell::new(next.borrow().to_vec())),
|
routes: Rc::new(RefCell::new(next.borrow().to_vec())),
|
||||||
root
|
root,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// show the root route
|
// show the root route
|
||||||
create_memo(cx, move |prev| {
|
let root = create_memo(cx, move |prev| {
|
||||||
provide_context(cx, route_states);
|
provide_context(cx, route_states);
|
||||||
route_states.with(|state| {
|
route_states.with(|state| {
|
||||||
let root = state.routes.borrow();
|
let root = state.routes.borrow();
|
||||||
|
@ -162,14 +185,20 @@ pub fn Routes(cx: Scope, props: RoutesProps) -> impl IntoChild {
|
||||||
}
|
}
|
||||||
|
|
||||||
if prev.is_none() || !root_equal.get() {
|
if prev.is_none() || !root_equal.get() {
|
||||||
root.as_ref().map(|route| {
|
root.as_ref().map(|route| route.outlet().into_child(cx))
|
||||||
route.outlet().into_child(cx)
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
prev.cloned().unwrap()
|
prev.cloned().unwrap()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
});
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(feature = "stable")] {
|
||||||
|
move || root.get()
|
||||||
|
} else {
|
||||||
|
root
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
|
|
@ -107,36 +107,51 @@ where
|
||||||
fn into_param(value: Option<&str>, name: &str) -> Result<Self, ParamsError>;
|
fn into_param(value: Option<&str>, name: &str) -> Result<Self, ParamsError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> IntoParam for Option<T>
|
cfg_if::cfg_if! {
|
||||||
where
|
if #[cfg(not(feature = "stable"))] {
|
||||||
T: FromStr,
|
auto trait NotOption {}
|
||||||
<T as FromStr>::Err: std::error::Error + 'static,
|
impl<T> !NotOption for Option<T> {}
|
||||||
{
|
|
||||||
fn into_param(value: Option<&str>, _name: &str) -> Result<Self, ParamsError> {
|
impl<T> IntoParam for T
|
||||||
match value {
|
where
|
||||||
None => Ok(None),
|
T: FromStr + NotOption,
|
||||||
Some(value) => match T::from_str(value) {
|
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
|
||||||
Ok(value) => Ok(Some(value)),
|
{
|
||||||
Err(e) => {
|
fn into_param(value: Option<&str>, name: &str) -> Result<Self, ParamsError> {
|
||||||
eprintln!("{}", e);
|
let value = value.ok_or_else(|| ParamsError::MissingParam(name.to_string()))?;
|
||||||
Err(ParamsError::Params(Rc::new(e)))
|
Self::from_str(value).map_err(|e| ParamsError::Params(Rc::new(e)))
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto trait NotOption {}
|
impl<T> IntoParam for Option<T>
|
||||||
impl<T> !NotOption for Option<T> {}
|
where
|
||||||
|
T: FromStr,
|
||||||
impl<T> IntoParam for T
|
<T as FromStr>::Err: std::error::Error + 'static,
|
||||||
where
|
{
|
||||||
T: FromStr + NotOption,
|
fn into_param(value: Option<&str>, _name: &str) -> Result<Self, ParamsError> {
|
||||||
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
|
match value {
|
||||||
{
|
None => Ok(None),
|
||||||
fn into_param(value: Option<&str>, name: &str) -> Result<Self, ParamsError> {
|
Some(value) => match T::from_str(value) {
|
||||||
let value = value.ok_or_else(|| ParamsError::MissingParam(name.to_string()))?;
|
Ok(value) => Ok(Some(value)),
|
||||||
Self::from_str(value).map_err(|e| ParamsError::Params(Rc::new(e)))
|
Err(e) => {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
Err(ParamsError::Params(Rc::new(e)))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
impl<T> IntoParam for T
|
||||||
|
where
|
||||||
|
T: FromStr,
|
||||||
|
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn into_param(value: Option<&str>, name: &str) -> Result<Self, ParamsError> {
|
||||||
|
let value = value.ok_or_else(|| ParamsError::MissingParam(name.to_string()))?;
|
||||||
|
Self::from_str(value).map_err(|e| ParamsError::Params(Rc::new(e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -138,10 +138,9 @@
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#![feature(auto_traits)]
|
#![cfg_attr(not(feature = "stable"), feature(auto_traits))]
|
||||||
#![feature(let_chains)]
|
#![cfg_attr(not(feature = "stable"), feature(negative_impls))]
|
||||||
#![feature(negative_impls)]
|
#![cfg_attr(not(feature = "stable"), feature(type_name_of_val))]
|
||||||
#![feature(type_name_of_val)]
|
|
||||||
|
|
||||||
mod components;
|
mod components;
|
||||||
mod history;
|
mod history;
|
||||||
|
|
|
@ -80,13 +80,15 @@ impl Matcher {
|
||||||
path.push_str(loc_segment);
|
path.push_str(loc_segment);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(splat) = &self.splat && !splat.is_empty() {
|
if let Some(splat) = &self.splat {
|
||||||
let value = if len_diff > 0 {
|
if !splat.is_empty() {
|
||||||
loc_segments[self.len..].join("/")
|
let value = if len_diff > 0 {
|
||||||
} else {
|
loc_segments[self.len..].join("/")
|
||||||
"".into()
|
} else {
|
||||||
};
|
"".into()
|
||||||
params.insert(splat.into(), value);
|
};
|
||||||
|
params.insert(splat.into(), value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(PathMatch { path, params })
|
Some(PathMatch { path, params })
|
||||||
|
|
Loading…
Reference in a new issue