Skip to main content

wal_core/router/
builder.rs

1use super::{not_found_component::NotFoundComponent, PageRenderer, Router};
2use crate::{
3    component::{node::AnyComponentNode, Component},
4    virtual_dom::dom,
5};
6use std::collections::HashMap;
7
8/// Builds application with routing.
9pub struct RouterBuilder {
10    pages: HashMap<&'static str, PageRenderer>,
11    not_found_page: Option<PageRenderer>,
12}
13
14impl Default for RouterBuilder {
15    /// Creates default [RouterBuilder].
16    fn default() -> Self {
17        RouterBuilder {
18            pages: HashMap::new(),
19            not_found_page: None,
20        }
21    }
22}
23
24impl RouterBuilder {
25    /// Adds provided page to application under provided path in variable path. Page is represented by **custom component** - struct implementing trait [Component] and [Default].
26    pub fn add_page<C>(mut self, path: &'static str) -> RouterBuilder
27    where
28        C: Component + Default + 'static,
29    {
30        let mut pages = self.pages;
31        pages.insert(
32            path,
33            PageRenderer::new(|| {
34                AnyComponentNode::new_root_routing(C::default(), dom::get_root_element())
35            }),
36        );
37        self.pages = pages;
38
39        // RouterBuilder {
40        //     pages,
41        //     not_found_page: self.not_found_page,
42        // }
43        self
44    }
45
46    /// Adds provided not found page to application.
47    /// All routes which cannot be resolved will be redirected to this page.
48    /// Adding more than one **not found page** results in undefined behavior.
49    pub fn add_not_found_page<C>(self) -> RouterBuilder
50    where
51        C: Component + Default + 'static,
52    {
53        RouterBuilder {
54            pages: self.pages,
55            not_found_page: Some(PageRenderer::new(|| {
56                AnyComponentNode::new_root_routing(C::default(), dom::get_root_element())
57            })),
58        }
59    }
60}
61
62impl RouterBuilder {
63    /// Builds [router](Router). If **not found page** was *not* specified, default one is provided.
64    pub fn build(self) -> Router {
65        if let Some(not_found_page) = self.not_found_page {
66            Router::new(self.pages, not_found_page)
67        } else {
68            Router::new(
69                self.pages,
70                PageRenderer::new(|| {
71                    AnyComponentNode::new_root_routing(NotFoundComponent, dom::get_root_element())
72                }),
73            )
74        }
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::RouterBuilder;
81    use crate::{
82        component::{behavior::Behavior, Component},
83        virtual_dom::{VNode, VText},
84    };
85    use wasm_bindgen_test::wasm_bindgen_test;
86    wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
87
88    const VALID_TEXT: &str = "";
89
90    #[wasm_bindgen_test]
91    fn invalid() {
92        let invalid = RouterBuilder::default();
93        assert!(invalid.not_found_page.is_none());
94    }
95
96    struct Root;
97    impl Default for Root {
98        fn default() -> Self {
99            Self::new(())
100        }
101    }
102    impl Component for Root {
103        type Message = ();
104        type Properties = ();
105        fn new(_props: Self::Properties) -> Self {
106            Root
107        }
108        fn view(&self, _behavior: &mut impl Behavior<Self>) -> VNode {
109            VText::new(VALID_TEXT).into()
110        }
111        fn update(&mut self, _message: Self::Message) -> bool {
112            false
113        }
114    }
115
116    #[wasm_bindgen_test]
117    fn valid_one_page() {
118        let valid = RouterBuilder::default().add_page::<Root>("/");
119        assert!(valid.pages.contains_key("/"));
120        assert_eq!(valid.pages.len(), 1);
121        assert!(valid.not_found_page.is_none());
122    }
123
124    struct Root2;
125    impl Default for Root2 {
126        fn default() -> Self {
127            Self::new(())
128        }
129    }
130    impl Component for Root2 {
131        type Message = ();
132        type Properties = ();
133        fn new(_props: Self::Properties) -> Self {
134            Root2
135        }
136        fn view(&self, _behavior: &mut impl Behavior<Self>) -> VNode {
137            VText::new(VALID_TEXT).into()
138        }
139        fn update(&mut self, _message: Self::Message) -> bool {
140            false
141        }
142    }
143
144    #[wasm_bindgen_test]
145    fn valid_multiple_pages() {
146        let valid = RouterBuilder::default()
147            .add_page::<Root>("/")
148            .add_page::<Root2>("/2");
149        assert!(valid.pages.contains_key("/"));
150        assert!(valid.pages.contains_key("/2"));
151        assert_eq!(valid.pages.len(), 2);
152        assert!(valid.not_found_page.is_none());
153    }
154
155    #[wasm_bindgen_test]
156    fn build_valid_multiple_pages() {
157        let valid = RouterBuilder::default()
158            .add_page::<Root>("/")
159            .add_page::<Root2>("/2");
160        assert!(valid.pages.contains_key("/"));
161        assert!(valid.pages.contains_key("/2"));
162        assert_eq!(valid.pages.len(), 2);
163        assert!(valid.not_found_page.is_none());
164
165        let router = valid.build();
166        assert!(router.pages.contains_key("/"));
167        assert!(router.pages.contains_key("/2"));
168        assert_eq!(router.pages.len(), 2);
169        assert!(router.current.is_none());
170    }
171
172    #[wasm_bindgen_test]
173    fn build_valid_custom_not_found_page() {
174        let valid = RouterBuilder::default().add_not_found_page::<Root>();
175        assert_eq!(valid.pages.len(), 0);
176        assert!(valid.not_found_page.is_some());
177
178        let router = valid.build();
179        assert_eq!(router.pages.len(), 0);
180        assert!(router.current.is_none());
181    }
182
183    #[wasm_bindgen_test]
184    fn build_valid_default_not_found_page() {
185        let path = "/";
186        let valid = RouterBuilder::default().add_page::<Root>(path);
187        assert!(valid.pages.contains_key(path));
188        assert_eq!(valid.pages.len(), 1);
189        assert!(valid.not_found_page.is_none());
190
191        let router = valid.build();
192        assert!(router.pages.contains_key(path));
193        assert_eq!(router.pages.len(), 1);
194        assert!(router.current.is_none());
195    }
196
197    #[wasm_bindgen_test]
198    fn build() {
199        let valid = RouterBuilder::default()
200            .add_page::<Root>("/")
201            .add_page::<Root2>("/2")
202            .build();
203
204        assert_eq!(valid.pages.len(), 2);
205        assert!(valid.pages.contains_key("/"));
206        assert!(valid.pages.contains_key("/2"));
207        assert!(valid.current.is_none());
208    }
209}