1use crate::route::Route;
4use crate::{NavigationDirection, RouteChangeEvent, RouteMatch};
5use std::collections::HashMap;
6use std::sync::Arc;
7
8#[derive(Debug, Clone)]
10pub struct RouterState {
11 history: Vec<String>,
13 current: usize,
15 routes: Vec<Arc<Route>>,
17 cache: HashMap<String, RouteMatch>,
19}
20
21impl RouterState {
22 pub fn new() -> Self {
24 Self {
25 history: vec!["/".to_string()],
26 current: 0,
27 routes: Vec::new(),
28 cache: HashMap::new(),
29 }
30 }
31
32 pub fn add_route(&mut self, route: Route) {
34 self.routes.push(Arc::new(route));
35 self.cache.clear();
37 }
38
39 pub fn current_path(&self) -> &str {
41 &self.history[self.current]
42 }
43
44 pub fn routes(&self) -> &[Arc<Route>] {
46 &self.routes
47 }
48
49 pub fn current_match(&mut self) -> Option<RouteMatch> {
51 let path = self.current_path();
52
53 if let Some(cached) = self.cache.get(path) {
55 return Some(cached.clone());
56 }
57
58 for route in &self.routes {
60 if let Some(route_match) = route.matches(path) {
61 self.cache.insert(path.to_string(), route_match.clone());
62 return Some(route_match);
63 }
64 }
65
66 None
67 }
68
69 pub fn current_match_immutable(&self) -> Option<RouteMatch> {
74 let path = self.current_path();
75
76 if let Some(cached) = self.cache.get(path) {
78 return Some(cached.clone());
79 }
80
81 for route in &self.routes {
83 if let Some(route_match) = route.matches(path) {
84 return Some(route_match);
85 }
86 }
87
88 None
89 }
90
91 pub fn current_route(&self) -> Option<&Arc<Route>> {
96 let path = self.current_path();
97
98 self.routes
99 .iter()
100 .find(|route| route.matches(path).is_some())
101 }
102
103 pub fn push(&mut self, path: String) -> RouteChangeEvent {
105 let from = Some(self.current_path().to_string());
106
107 self.history.truncate(self.current + 1);
109
110 self.history.push(path.clone());
112 self.current += 1;
113
114 RouteChangeEvent {
115 from,
116 to: path,
117 direction: NavigationDirection::Forward,
118 }
119 }
120
121 pub fn replace(&mut self, path: String) -> RouteChangeEvent {
123 let from = Some(self.current_path().to_string());
124
125 self.history[self.current] = path.clone();
126
127 RouteChangeEvent {
128 from,
129 to: path,
130 direction: NavigationDirection::Replace,
131 }
132 }
133
134 pub fn back(&mut self) -> Option<RouteChangeEvent> {
136 if self.current > 0 {
137 let from = Some(self.current_path().to_string());
138 self.current -= 1;
139 let to = self.current_path().to_string();
140
141 Some(RouteChangeEvent {
142 from,
143 to,
144 direction: NavigationDirection::Back,
145 })
146 } else {
147 None
148 }
149 }
150
151 pub fn forward(&mut self) -> Option<RouteChangeEvent> {
153 if self.current < self.history.len() - 1 {
154 let from = Some(self.current_path().to_string());
155 self.current += 1;
156 let to = self.current_path().to_string();
157
158 Some(RouteChangeEvent {
159 from,
160 to,
161 direction: NavigationDirection::Forward,
162 })
163 } else {
164 None
165 }
166 }
167
168 pub fn can_go_back(&self) -> bool {
170 self.current > 0
171 }
172
173 pub fn can_go_forward(&self) -> bool {
175 self.current < self.history.len() - 1
176 }
177
178 pub fn clear(&mut self) {
180 self.history.clear();
181 self.history.push("/".to_string());
182 self.current = 0;
183 self.cache.clear();
184 }
185}
186
187impl Default for RouterState {
188 fn default() -> Self {
189 Self::new()
190 }
191}
192
193pub struct Router {
195 state: RouterState,
196}
197
198impl Router {
199 pub fn new() -> Self {
201 Self {
202 state: RouterState::new(),
203 }
204 }
205
206 pub fn state_mut(&mut self) -> &mut RouterState {
208 &mut self.state
209 }
210
211 pub fn state(&self) -> &RouterState {
213 &self.state
214 }
215}
216
217impl Default for Router {
218 fn default() -> Self {
219 Self::new()
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn test_navigation() {
229 let mut state = RouterState::new();
230
231 assert_eq!(state.current_path(), "/");
232
233 state.push("/users".to_string());
234 assert_eq!(state.current_path(), "/users");
235
236 state.push("/users/123".to_string());
237 assert_eq!(state.current_path(), "/users/123");
238
239 state.back();
240 assert_eq!(state.current_path(), "/users");
241
242 state.forward();
243 assert_eq!(state.current_path(), "/users/123");
244 }
245
246 #[test]
247 fn test_replace() {
248 let mut state = RouterState::new();
249
250 state.push("/users".to_string());
251 state.replace("/posts".to_string());
252
253 assert_eq!(state.current_path(), "/posts");
254 assert_eq!(state.history.len(), 2);
255 }
256}