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