Browse Source

create cfg factory

Evan Almloff 2 years ago
parent
commit
67992f7da9

+ 1 - 12
packages/router/examples/simple_routes.rs

@@ -14,18 +14,7 @@ fn main() {
 
 fn root(cx: Scope) -> Element {
     render! {
-        Router {
-            config: RouterConfiguration {
-                history: {
-                    #[cfg(not(target_arch = "wasm32"))]
-                    let history = Box::<MemoryHistory::<Route>>::default();
-                    #[cfg(target_arch = "wasm32")]
-                    let history = Box::<WebHistory::<Route>>::default();
-                    history
-                },
-                ..Default::default()
-            }
-        }
+        Router {}
     }
 }
 

+ 16 - 11
packages/router/src/components/router.rs

@@ -9,25 +9,26 @@ use crate::{
 };
 
 /// The config for [`GenericRouter`].
-pub struct RouterCfg<R: Routable> {
-    config: RefCell<Option<RouterConfiguration<R>>>,
+pub struct RouterConfigFactory<R: Routable> {
+    #[allow(clippy::type_complexity)]
+    config: RefCell<Option<Box<dyn FnOnce() -> RouterConfiguration<R>>>>,
 }
 
-impl<R: Routable> Default for RouterCfg<R>
+impl<R: Routable> Default for RouterConfigFactory<R>
 where
     <R as FromStr>::Err: std::fmt::Display,
 {
     fn default() -> Self {
-        Self {
-            config: RefCell::new(Some(RouterConfiguration::default())),
-        }
+        Self::from(RouterConfiguration::default)
     }
 }
 
-impl<R: Routable> From<RouterConfiguration<R>> for RouterCfg<R> {
-    fn from(value: RouterConfiguration<R>) -> Self {
+impl<R: Routable, F: FnOnce() -> RouterConfiguration<R> + 'static> From<F>
+    for RouterConfigFactory<R>
+{
+    fn from(value: F) -> Self {
         Self {
-            config: RefCell::new(Some(value)),
+            config: RefCell::new(Some(Box::new(value))),
         }
     }
 }
@@ -39,7 +40,7 @@ where
     <R as FromStr>::Err: std::fmt::Display,
 {
     #[props(default, into)]
-    config: RouterCfg<R>,
+    config: RouterConfigFactory<R>,
 }
 
 impl<R: Routable> PartialEq for GenericRouterProps<R>
@@ -66,7 +67,11 @@ where
             panic!("{}", msg);
         }
         let router = GenericRouterContext::new(
-            cx.props.config.config.take().unwrap_or_default(),
+            (cx.props
+                .config
+                .config
+                .take()
+                .expect("use_context_provider ran twice"))(),
             cx.schedule_update_any(),
         );
         router

+ 1 - 1
packages/router/src/contexts/router.rs

@@ -15,7 +15,7 @@ use crate::{
 pub struct ExternalNavigationFailure(String);
 
 /// A function the router will call after every routing update.
-pub type RoutingCallback<R> = Arc<dyn Fn(GenericRouterContext<R>) -> Option<NavigationTarget<R>>>;
+pub(crate) type RoutingCallback<R> = Arc<dyn Fn(GenericRouterContext<R>) -> Option<NavigationTarget<R>>>;
 
 struct MutableRouterState<R>
 where

+ 54 - 23
packages/router/src/router_cfg.rs

@@ -1,3 +1,5 @@
+use std::sync::Arc;
+
 use crate::contexts::router::RoutingCallback;
 use crate::history::HistoryProvider;
 use crate::routable::Routable;
@@ -7,7 +9,7 @@ use crate::prelude::*;
 
 /// Global configuration options for the router.
 ///
-/// This implements [`Default`], so you can use it like this:
+/// This implements [`Default`] and follows the builder pattern, so you can use it like this:
 /// ```rust,no_run
 /// # use dioxus_router::prelude::*;
 /// # use serde::{Deserialize, Serialize};
@@ -21,20 +23,34 @@ use crate::prelude::*;
 ///     #[route("/")]
 ///     Index {},
 /// }
-/// let cfg = RouterConfiguration {
-///     history: Box::<WebHistory<Route>>::default(),
-///     ..Default::default()
-/// };
+/// let cfg = RouterConfiguration::default().history(WebHistory<Route>::default());
 /// ```
 pub struct RouterConfiguration<R: Routable> {
-    /// A component to render when an external navigation fails.
-    ///
-    /// Defaults to a router-internal component called [`FailureExternalNavigation`]
-    pub failure_external_navigation: fn(Scope) -> Element,
-    /// The [`HistoryProvider`] the router should use.
-    ///
-    /// Defaults to a default [`MemoryHistory`].
-    pub history: Box<dyn HistoryProvider<R>>,
+    pub(crate) failure_external_navigation: fn(Scope) -> Element,
+    pub(crate) history: Box<dyn HistoryProvider<R>>,
+    pub(crate) on_update: Option<RoutingCallback<R>>,
+}
+
+impl<R: Routable + Clone> Default for RouterConfiguration<R>
+where
+    <R as std::str::FromStr>::Err: std::fmt::Display,
+{
+    fn default() -> Self {
+        Self {
+            failure_external_navigation: FailureExternalNavigation::<R>,
+            history: {
+                #[cfg(all(target_arch = "wasm32", feature = "web"))]
+                let history = Box::<MemoryHistory<R>>::default();
+                #[cfg(not(all(target_arch = "wasm32", feature = "web")))]
+                let history = Box::<MemoryHistory<R>>::default();
+                history
+            },
+            on_update: None,
+        }
+    }
+}
+
+impl<R: Routable> RouterConfiguration<R> {
     /// A function to be called whenever the routing is updated.
     ///
     /// The callback is invoked after the routing is updated, but before components and hooks are
@@ -48,18 +64,33 @@ pub struct RouterConfiguration<R: Routable> {
     /// navigation failure occurs.
     ///
     /// Defaults to [`None`].
-    pub on_update: Option<RoutingCallback<R>>,
-}
+    pub fn on_update(
+        self,
+        callback: impl Fn(GenericRouterContext<R>) -> Option<NavigationTarget<R>> + 'static,
+    ) -> Self {
+        Self {
+            on_update: Some(Arc::new(callback)),
+            ..self
+        }
+    }
 
-impl<R: Routable + Clone> Default for RouterConfiguration<R>
-where
-    <R as std::str::FromStr>::Err: std::fmt::Display,
-{
-    fn default() -> Self {
+    /// The [`HistoryProvider`] the router should use.
+    ///
+    /// Defaults to a default [`MemoryHistory`].
+    pub fn history(self, history: impl HistoryProvider<R> + 'static) -> Self {
         Self {
-            failure_external_navigation: FailureExternalNavigation::<R>,
-            history: Box::<MemoryHistory<R>>::default(),
-            on_update: None,
+            history: Box::new(history),
+            ..self
+        }
+    }
+
+    /// A component to render when an external navigation fails.
+    ///
+    /// Defaults to a router-internal component called [`FailureExternalNavigation`]
+    pub fn failure_external_navigation(self, component: fn(Scope) -> Element) -> Self {
+        Self {
+            failure_external_navigation: component,
+            ..self
         }
     }
 }

+ 12 - 16
packages/router/tests/via_ssr/link.rs

@@ -1,10 +1,9 @@
 #![allow(non_snake_case)]
 use dioxus::prelude::*;
 use dioxus_router::prelude::*;
-use serde::{de::DeserializeOwned, Deserialize, Serialize};
 use std::str::FromStr;
 
-fn prepare<R: Routable + Serialize + DeserializeOwned>() -> String
+fn prepare<R: Routable>() -> String
 where
     <R as FromStr>::Err: std::fmt::Display,
 {
@@ -28,17 +27,14 @@ where
         }
     }
 
-    fn App<R: Routable + Serialize + DeserializeOwned>(cx: Scope<AppProps<R>>) -> Element
+    fn App<R: Routable>(cx: Scope<AppProps<R>>) -> Element
     where
         <R as FromStr>::Err: std::fmt::Display,
     {
         render! {
             h1 { "App" }
             GenericRouter::<R> {
-                config: RouterConfiguration {
-                    history: Box::<MemoryHistory::<R>>::default(),
-                    ..Default::default()
-                }
+                config: || RouterConfiguration::default().history(MemoryHistory::default())
             }
         }
     }
@@ -46,7 +42,7 @@ where
 
 #[test]
 fn href_internal() {
-    #[derive(Routable, Clone, Serialize, Deserialize)]
+    #[derive(Routable, Clone)]
     enum Route {
         #[route("/")]
         Root {},
@@ -84,7 +80,7 @@ fn href_internal() {
 
 #[test]
 fn href_external() {
-    #[derive(Routable, Clone, Serialize, Deserialize)]
+    #[derive(Routable, Clone)]
     enum Route {
         #[route("/")]
         Root {},
@@ -122,7 +118,7 @@ fn href_external() {
 
 #[test]
 fn with_class() {
-    #[derive(Routable, Clone, Serialize, Deserialize)]
+    #[derive(Routable, Clone)]
     enum Route {
         #[route("/")]
         Root {},
@@ -161,7 +157,7 @@ fn with_class() {
 
 #[test]
 fn with_active_class_active() {
-    #[derive(Routable, Clone, Serialize, Deserialize)]
+    #[derive(Routable, Clone)]
     enum Route {
         #[route("/")]
         Root {},
@@ -194,7 +190,7 @@ fn with_active_class_active() {
 
 #[test]
 fn with_active_class_inactive() {
-    #[derive(Routable, Clone, Serialize, Deserialize)]
+    #[derive(Routable, Clone)]
     enum Route {
         #[route("/")]
         Root {},
@@ -234,7 +230,7 @@ fn with_active_class_inactive() {
 
 #[test]
 fn with_id() {
-    #[derive(Routable, Clone, Serialize, Deserialize)]
+    #[derive(Routable, Clone)]
     enum Route {
         #[route("/")]
         Root {},
@@ -273,7 +269,7 @@ fn with_id() {
 
 #[test]
 fn with_new_tab() {
-    #[derive(Routable, Clone, Serialize, Deserialize)]
+    #[derive(Routable, Clone)]
     enum Route {
         #[route("/")]
         Root {},
@@ -312,7 +308,7 @@ fn with_new_tab() {
 
 #[test]
 fn with_new_tab_external() {
-    #[derive(Routable, Clone, Serialize, Deserialize)]
+    #[derive(Routable, Clone)]
     enum Route {
         #[route("/")]
         Root {},
@@ -344,7 +340,7 @@ fn with_new_tab_external() {
 
 #[test]
 fn with_rel() {
-    #[derive(Routable, Clone, Serialize, Deserialize)]
+    #[derive(Routable, Clone)]
     enum Route {
         #[route("/")]
         Root {},

+ 5 - 8
packages/router/tests/via_ssr/outlet.rs

@@ -2,14 +2,13 @@
 
 use dioxus::prelude::*;
 use dioxus_router::prelude::*;
-use serde::{Deserialize, Serialize};
 
 fn prepare(path: impl Into<String>) -> VirtualDom {
     let mut vdom = VirtualDom::new_with_props(App, AppProps { path: path.into() });
     let _ = vdom.rebuild();
     return vdom;
 
-    #[derive(Routable, Clone, Serialize, Deserialize)]
+    #[derive(Routable, Clone)]
     #[rustfmt::skip]
     enum Route {
         #[route("/")]
@@ -36,15 +35,13 @@ fn prepare(path: impl Into<String>) -> VirtualDom {
     }
 
     fn App(cx: Scope<AppProps>) -> Element {
-        let cfg = RouterConfiguration {
-            history: Box::new(MemoryHistory::with_initial_path(cx.props.path.clone()).unwrap()),
-            ..Default::default()
-        };
-
         render! {
             h1 { "App" }
             Router {
-                config: cfg
+                config: {
+                    let path = cx.props.path.clone();
+                    move || RouterConfiguration::default().history(MemoryHistory::with_initial_path(path).unwrap())
+                }
             }
         }
     }