euv_ui/component/router/impl.rs
1//! Nested route matching utility.
2//!
3//! Provides a pure-data route matcher that supports
4//! nested route configurations (parent routes with
5//! child layouts). The router UI component (in
6//! `ui/src/component/router/`) can use this utility
7//! to resolve the active route against a tree of
8//! route configs and render the matched chain.
9//!
10//! # Why pure data?
11//!
12//! The matcher is just a recursive walk over a tree of
13//! route configs. No DOM access, no signal subscription,
14//! no reactive dependency. This lets us:
15//!
16//! - Cover every match branch with native unit tests.
17//! - Reuse the matcher in non-browser contexts (SSR,
18//! tests, snapshot tests in CI).
19//!
20//! # API
21//!
22//! ```ignore
23//! use euv_ui::component::router::nested::{NestedRouteConfig, find_active_route};
24//!
25//! let routes: Vec<NestedRouteConfig> = vec![
26//! NestedRouteConfig::new(
27//! "/",
28//! || html! { div { "Home" } },
29//! vec![],
30//! ),
31//! NestedRouteConfig::new(
32//! "/settings",
33//! || html! { div { "Settings layout" } },
34//! vec![
35//! NestedRouteConfig::new(
36//! "/settings/profile",
37//! || html! { div { "Profile" } },
38//! vec![],
39//! ),
40//! ],
41//! ),
42//! ];
43//!
44//! let active: Option<&NestedRouteConfig> =
45//! find_active_route("/settings/profile", &routes);
46//! // `active` points to the /settings/profile route.
47//! ```
48//!
49//! # Path matching rules
50//!
51//! - An exact path match wins over a prefix match.
52//! - Trailing slashes are normalized away before
53//! matching: `/settings/` and `/settings` are
54//! treated as the same path.
55//! - Empty paths and `/` are treated as the root
56//! route.
57use super::*;
58
59impl NestedRouteConfig {
60 /// Creates a new nested route configuration.
61 ///
62 /// # Arguments
63 ///
64 /// - `path: impl Into<String>` - The route path.
65 /// - `component: F` - The component closure.
66 /// - `children: Vec<NestedRouteConfig>` - The child
67 /// routes.
68 pub fn new<F>(path: impl Into<String>, component: F, children: Vec<NestedRouteConfig>) -> Self
69 where
70 F: Fn() -> VirtualNode + 'static,
71 {
72 Self {
73 path: path.into(),
74 component: Rc::new(component),
75 children,
76 }
77 }
78
79 /// Returns the component closure.
80 pub fn component(&self) -> Rc<dyn Fn() -> VirtualNode> {
81 self.component.clone()
82 }
83
84 /// Returns the child routes.
85 pub fn children(&self) -> &[NestedRouteConfig] {
86 &self.children
87 }
88}