From 927ccfa0230b787339289c5f4f08a58d087c7413 Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Mon, 4 Nov 2019 11:43:10 -0500 Subject: [PATCH 1/3] support full redirects --- crates/yew_router_macro/src/lib.rs | 4 +- examples/minimal/src/main.rs | 2 +- examples/router_component/src/b_component.rs | 2 +- examples/router_component/src/main.rs | 4 +- examples/switch/src/main.rs | 8 +-- src/router.rs | 38 ++++++++----- src/switch.rs | 4 +- tests/macro_test/src/lib.rs | 58 ++++++++++---------- 8 files changed, 64 insertions(+), 56 deletions(-) diff --git a/crates/yew_router_macro/src/lib.rs b/crates/yew_router_macro/src/lib.rs index 9bef84d..4f407bf 100644 --- a/crates/yew_router_macro/src/lib.rs +++ b/crates/yew_router_macro/src/lib.rs @@ -54,7 +54,7 @@ mod switch; /// ``` /// use yew_router::Switch; /// -/// #[derive(Switch)] +/// #[derive(Switch, Clone)] /// enum AppRoute { /// #[to = "/some/simple/route"] /// SomeSimpleRoute, @@ -68,7 +68,7 @@ mod switch; /// Inner(InnerRoute), /// } /// -/// #[derive(Switch)] +/// #[derive(Switch, Clone)] /// #[to = "/inner/route/{first}/{second}"] /// struct InnerRoute { /// first: String, diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 1e9d2bc..a3a4216 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -24,7 +24,7 @@ pub enum Msg { ChangeRoute(AppRoute), } -#[derive(Debug, Switch)] +#[derive(Debug, Switch, Clone)] pub enum AppRoute { #[to = "/a/{anything}"] A(String), diff --git a/examples/router_component/src/b_component.rs b/examples/router_component/src/b_component.rs index eec8982..67faf28 100644 --- a/examples/router_component/src/b_component.rs +++ b/examples/router_component/src/b_component.rs @@ -15,7 +15,7 @@ pub struct Props { pub sub_path: Option, } -#[derive(Debug, Switch)] +#[derive(Debug, Switch, Clone)] pub enum BRoute { #[to = "/{num}?sup_path={sub_path}"] Both(usize, String), diff --git a/examples/router_component/src/main.rs b/examples/router_component/src/main.rs index f170635..74b04ea 100644 --- a/examples/router_component/src/main.rs +++ b/examples/router_component/src/main.rs @@ -76,7 +76,7 @@ impl Component for Model { } } -#[derive(Debug, Switch)] +#[derive(Debug, Switch, Clone)] pub enum AppRoute { #[to = "/a{*:inner}"] A(AllowMissing), @@ -90,6 +90,6 @@ pub enum AppRoute { PageNotFound(Option), } -#[derive(Debug, Switch, PartialEq, Clone)] +#[derive(Debug, Switch, PartialEq, Clone, Copy)] #[to = "/c"] pub struct ARoute; diff --git a/examples/switch/src/main.rs b/examples/switch/src/main.rs index 7d8df14..641cda1 100644 --- a/examples/switch/src/main.rs +++ b/examples/switch/src/main.rs @@ -50,7 +50,7 @@ fn main() { println!("{}", buf); } -#[derive(Debug, Switch)] +#[derive(Debug, Switch, Clone)] pub enum AppRoute { #[to = "/some/route"] SomeRoute, @@ -77,7 +77,7 @@ pub enum AppRoute { MissingCapture(Option), } -#[derive(Switch, Debug)] +#[derive(Switch, Debug, Clone)] pub enum InnerRoute { #[to = "/left"] Left, @@ -85,13 +85,13 @@ pub enum InnerRoute { Right, } -#[derive(Switch, Debug)] +#[derive(Switch, Debug, Clone)] #[to = "/single/{number}"] pub struct Single { number: u32, } -#[derive(Switch, Debug)] +#[derive(Switch, Debug, Clone)] #[to = "/othersingle/{number}"] pub struct OtherSingle(u32); diff --git a/src/router.rs b/src/router.rs index 5fcf206..188c84a 100644 --- a/src/router.rs +++ b/src/router.rs @@ -55,7 +55,7 @@ impl<'de, T> RouterState<'de> for T where T: AgentState<'de> + PartialEq {} /// } /// } /// -/// #[derive(Switch)] +/// #[derive(Switch, Clone)] /// enum S { /// #[to = "/v"] /// Variant, @@ -63,7 +63,7 @@ impl<'de, T> RouterState<'de> for T where T: AgentState<'de> + PartialEq {} /// ``` #[derive(Debug)] pub struct Router RouterState<'de>, SW: Switch + 'static, M: 'static> { - route: Route, + switch: Option, props: Props, router_agent: RouteAgentBridge, } @@ -74,13 +74,14 @@ where SW: Switch + 'static, M: 'static, { + // TODO render fn name is overloaded now with that of the trait: Renderable<_> this should be changed. Maybe: display, show, switch, inner... /// Wrap a render closure so that it can be used by the Router. /// # Example /// ``` /// # use yew_router::Switch; /// # use yew_router::router::Router; /// # use yew::{html, Html}; - /// # #[derive(Switch)] + /// # #[derive(Switch, Clone)] /// # enum S { /// # #[to = "/route"] /// # Variant @@ -194,7 +195,7 @@ where let router_agent = RouteAgentBridge::new(callback); Router { - route: Default::default(), /* This must be updated by immediately requesting a route + switch: Default::default(), /* This must be updated by immediately requesting a route * update from the service bridge. */ props, router_agent, @@ -209,9 +210,21 @@ where fn update(&mut self, msg: Self::Message) -> ShouldRender { match msg { Msg::UpdateRoute(route) => { - let did_change = self.route != route; - self.route = route; - did_change + let mut switch = SW::switch(route.clone()); + + if switch.is_none() { + if let Some(redirect) = &self.props.redirect { + let redirected: SW = (&redirect.0)(route); + + log::trace!("Route failed to match, but redirecting route to a known switch."); + // Replace the route in the browser with the redirected. + self.router_agent.send(RouteRequest::ReplaceRouteNoBroadcast(redirected.clone().into())); + switch = Some(redirected) + } + } + + self.switch = switch; + true } Msg::InnerMessage(m) => { if let Some(cb) = &self.props.callback { @@ -228,16 +241,11 @@ where } fn view(&self) -> VNode { - let switch: Option = SW::switch(self.route.clone()); - match switch { + match self.switch.clone() { Some(switch) => (&self.props.render.0)(switch), None => { - if let Some(redirect_fn) = &self.props.redirect { - let switch: SW = (redirect_fn.0)(self.route.clone()); // TODO This should be used to set the route in the browser https://github.com/yewstack/yew_router/issues/171 - (&self.props.render.0)(switch) - } else { - html! {format!{"No route for {}", self.route.route}} - } + log::warn!("No route matched, provide a redirect prop to the router to handle cases where no route can be matched"); + html! {"No route matched"} } } } diff --git a/src/switch.rs b/src/switch.rs index f6cc556..82f501a 100644 --- a/src/switch.rs +++ b/src/switch.rs @@ -10,7 +10,7 @@ use std::fmt::Write; /// # Example /// ``` /// use yew_router::{route::Route, Switch}; -/// #[derive(Debug, Switch, PartialEq)] +/// #[derive(Debug, Switch, Clone, PartialEq)] /// enum TestEnum { /// #[to = "/test/route"] /// TestRoute, @@ -41,7 +41,7 @@ use std::fmt::Write; /// Some(TestEnum::CaptureUnnamed("lorem".to_string())) /// ); /// ``` -pub trait Switch: Sized { +pub trait Switch: Clone + Sized { /// Based on a route, possibly produce an itself. fn switch(route: Route) -> Option { Self::from_route_part(route).0 diff --git a/tests/macro_test/src/lib.rs b/tests/macro_test/src/lib.rs index f03831b..11ce833 100644 --- a/tests/macro_test/src/lib.rs +++ b/tests/macro_test/src/lib.rs @@ -4,7 +4,7 @@ mod tests { #[test] fn single_enum_variant() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/variant"] Variant, @@ -16,7 +16,7 @@ mod tests { #[test] fn single_enum_variant_unnamed_without_corresponding_capture_group() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/variant"] Variant(String), @@ -35,7 +35,7 @@ mod tests { #[test] fn single_enum_variant_named_capture() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/variant/{item}"] Variant { item: String }, @@ -52,7 +52,7 @@ mod tests { #[test] fn single_enum_variant_unnamed_capture() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/variant/{item}"] Variant(String), @@ -64,7 +64,7 @@ mod tests { #[test] fn single_enum_variant_multiple_unnamed_capture() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/variant/{}/{}"] // For unnamed variants, the names don't matter at all Variant(String, String), @@ -79,7 +79,7 @@ mod tests { #[test] fn single_enum_variant_multiple_named_capture() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/variant/{item1}/{item2}"] Variant { item1: String, item2: String }, @@ -97,7 +97,7 @@ mod tests { #[test] fn single_enum_variant_named_capture_without_leading_separator() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/variant{item}"] Variant { item: String }, @@ -114,7 +114,7 @@ mod tests { #[test] fn single_enum_variant_named_capture_without_any_separator() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/variant{item}stuff"] Variant { item: String }, @@ -131,7 +131,7 @@ mod tests { #[test] fn single_enum_variant_end() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/variant!"] Variant, @@ -142,7 +142,7 @@ mod tests { #[test] fn multiple_enum_variant_end_precedence() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/variant!"] Variant1, @@ -160,7 +160,7 @@ mod tests { #[test] fn multiple_enum_variant_eager_matching() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/variant"] Variant1, @@ -178,7 +178,7 @@ mod tests { #[test] fn single_enum_variant_convert_usize() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/variant/{item}"] Variant(usize), @@ -190,7 +190,7 @@ mod tests { #[test] fn single_enum_variant_convert_usize_rejects_negative() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/variant/{item}"] Variant(usize), @@ -201,7 +201,7 @@ mod tests { #[test] fn single_enum_variant_convert_isize() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/variant/{item}"] Variant(isize), @@ -213,7 +213,7 @@ mod tests { #[test] fn single_enum_variant_missing_cap_produces_option_none() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/variant"] Variant(Option), @@ -239,7 +239,7 @@ mod tests { #[test] fn leading_slash() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/"] Variant, @@ -251,7 +251,7 @@ mod tests { #[test] fn leading_named_capture() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "{cap}"] Variant(String), @@ -263,7 +263,7 @@ mod tests { #[test] fn leading_unnamed_capture() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "{}"] Variant(String), @@ -275,7 +275,7 @@ mod tests { #[test] fn leading_number_capture() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "{2:cap}"] Variant(String), @@ -287,7 +287,7 @@ mod tests { #[test] fn leading_number_capture_unnamed() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "{2}"] Variant(String), @@ -299,7 +299,7 @@ mod tests { #[test] fn leading_many_capture_named() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "{*:cap}"] Variant(String), @@ -311,7 +311,7 @@ mod tests { #[test] fn leading_many_capture_unnamed() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "{*}"] Variant(String), @@ -323,7 +323,7 @@ mod tests { #[test] fn leading_query_named() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "?query={hello}"] Variant(String), @@ -335,7 +335,7 @@ mod tests { #[test] fn leading_query_unnamed() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "?query={}"] Variant(String), @@ -347,7 +347,7 @@ mod tests { #[test] fn leading_fragment() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "#fragment"] Variant, @@ -359,7 +359,7 @@ mod tests { #[test] fn fragment_with_named_captures() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "#{cap}ipsum{cap}"] Variant(String, String), @@ -374,7 +374,7 @@ mod tests { #[test] fn fragment_with_unnamed_captures() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "#{}ipsum{}"] Variant(String, String), @@ -389,7 +389,7 @@ mod tests { #[test] fn escape_exclaim() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, PartialEq, Clone)] pub enum Test { #[to = "/escape!!"] Variant, @@ -401,7 +401,7 @@ mod tests { #[test] fn escape_bracket() { - #[derive(Debug, Switch, PartialEq)] + #[derive(Debug, Switch, Clone, PartialEq)] pub enum Test { #[to = "/escape{{}}a"] Variant, From 8ee9ea2f12009b705ca8ace94c342887cfad3cd2 Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Mon, 4 Nov 2019 11:54:12 -0500 Subject: [PATCH 2/3] Clone now only required on Switches to be used in Router component --- src/router.rs | 4 ++-- src/switch.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/router.rs b/src/router.rs index 188c84a..c03bdb0 100644 --- a/src/router.rs +++ b/src/router.rs @@ -62,7 +62,7 @@ impl<'de, T> RouterState<'de> for T where T: AgentState<'de> + PartialEq {} /// } /// ``` #[derive(Debug)] -pub struct Router RouterState<'de>, SW: Switch + 'static, M: 'static> { +pub struct Router RouterState<'de>, SW: Switch + Clone + 'static, M: 'static> { switch: Option, props: Props, router_agent: RouteAgentBridge, @@ -71,7 +71,7 @@ pub struct Router RouterState<'de>, SW: Switch + 'static, M: 'static impl Router where T: for<'de> RouterState<'de>, - SW: Switch + 'static, + SW: Switch + Clone + 'static, M: 'static, { // TODO render fn name is overloaded now with that of the trait: Renderable<_> this should be changed. Maybe: display, show, switch, inner... diff --git a/src/switch.rs b/src/switch.rs index 82f501a..906087a 100644 --- a/src/switch.rs +++ b/src/switch.rs @@ -10,7 +10,7 @@ use std::fmt::Write; /// # Example /// ``` /// use yew_router::{route::Route, Switch}; -/// #[derive(Debug, Switch, Clone, PartialEq)] +/// #[derive(Debug, Switch, PartialEq)] /// enum TestEnum { /// #[to = "/test/route"] /// TestRoute, From 554843cb75fcc97d63fa033c0c093137e17b2811 Mon Sep 17 00:00:00 2001 From: Henry Zimmerman Date: Mon, 4 Nov 2019 12:05:22 -0500 Subject: [PATCH 3/3] properly implement new clone requirement --- src/router.rs | 14 +++++++------- src/switch.rs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/router.rs b/src/router.rs index c03bdb0..2b7b2f7 100644 --- a/src/router.rs +++ b/src/router.rs @@ -62,7 +62,7 @@ impl<'de, T> RouterState<'de> for T where T: AgentState<'de> + PartialEq {} /// } /// ``` #[derive(Debug)] -pub struct Router RouterState<'de>, SW: Switch + Clone + 'static, M: 'static> { +pub struct Router RouterState<'de>, SW: Switch + Clone + 'static, M: 'static> { switch: Option, props: Props, router_agent: RouteAgentBridge, @@ -125,16 +125,16 @@ impl From for Msg { pub trait RenderFn: Fn(SW) -> Html {} impl RenderFn for T where T: Fn(SW) -> Html {} /// Owned Render function. -pub struct Render RouterState<'de>, SW: Switch + 'static, M: 'static>( +pub struct Render RouterState<'de>, SW: Switch + Clone+ 'static, M: 'static>( pub(crate) Rc, SW>>, ); -impl RouterState<'de>, SW: Switch, M> Render { +impl RouterState<'de>, SW: Switch + Clone, M> Render { /// New render function fn new, SW> + 'static>(f: F) -> Self { Render(Rc::new(f)) } } -impl RouterState<'de>, SW: Switch, M> Debug for Render { +impl RouterState<'de>, SW: Switch + Clone, M> Debug for Render { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("Render").finish() } @@ -163,7 +163,7 @@ impl RouterState<'de>, SW: Switch, M> Debug for Redirect RouterState<'de>, SW: Switch + 'static, M: 'static> { +pub struct Props RouterState<'de>, SW: Switch + Clone + 'static, M: 'static> { /// Render function that #[props(required)] pub render: Render, @@ -175,7 +175,7 @@ pub struct Props RouterState<'de>, SW: Switch + 'static, M: 'static> pub callback: Option>, } -impl RouterState<'de>, SW: Switch, M> Debug for Props { +impl RouterState<'de>, SW: Switch + Clone, M> Debug for Props { fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { f.debug_struct("Props").finish() } @@ -184,7 +184,7 @@ impl RouterState<'de>, SW: Switch, M> Debug for Props { impl Component for Router where T: for<'de> RouterState<'de>, - SW: Switch + 'static, + SW: Switch + Clone + 'static, M: 'static, { type Message = Msg; diff --git a/src/switch.rs b/src/switch.rs index 906087a..f6cc556 100644 --- a/src/switch.rs +++ b/src/switch.rs @@ -41,7 +41,7 @@ use std::fmt::Write; /// Some(TestEnum::CaptureUnnamed("lorem".to_string())) /// ); /// ``` -pub trait Switch: Clone + Sized { +pub trait Switch: Sized { /// Based on a route, possibly produce an itself. fn switch(route: Route) -> Option { Self::from_route_part(route).0