1use super::tree::Tree;
2use super::{IntoPattern, Resource, ResourceDef, ResourcePath};
3
4#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5pub struct ResourceId(u16);
6
7#[derive(Debug, Clone)]
9pub struct Router<T, U = ()> {
10 tree: Tree,
11 resources: Vec<(ResourceDef, T, Option<U>)>,
12 insensitive: bool,
13}
14
15impl<T, U> Router<T, U> {
16 pub fn build() -> RouterBuilder<T, U> {
17 RouterBuilder {
18 resources: Vec::new(),
19 insensitive: false,
20 }
21 }
22
23 pub fn recognize<R, P>(&self, resource: &mut R) -> Option<(&T, ResourceId)>
24 where
25 R: Resource<P>,
26 P: ResourcePath,
27 {
28 if let Some(idx) = if self.insensitive {
29 self.tree.find_insensitive(resource)
30 } else {
31 self.tree.find(resource)
32 } {
33 let item = &self.resources[idx];
34 Some((&item.1, ResourceId(item.0.id())))
35 } else {
36 None
37 }
38 }
39
40 pub fn recognize_mut<R, P>(&mut self, resource: &mut R) -> Option<(&mut T, ResourceId)>
41 where
42 R: Resource<P>,
43 P: ResourcePath,
44 {
45 if let Some(idx) = if self.insensitive {
46 self.tree.find_insensitive(resource)
47 } else {
48 self.tree.find(resource)
49 } {
50 let item = &mut self.resources[idx];
51 Some((&mut item.1, ResourceId(item.0.id())))
52 } else {
53 None
54 }
55 }
56
57 pub fn recognize_checked<R, P, F>(
58 &self,
59 resource: &mut R,
60 check: F,
61 ) -> Option<(&T, ResourceId)>
62 where
63 F: Fn(&R, Option<&U>) -> bool,
64 R: Resource<P>,
65 P: ResourcePath,
66 {
67 if let Some(idx) = if self.insensitive {
68 self.tree.find_checked_insensitive(resource, &|idx, res| {
69 let item = &self.resources[idx];
70 check(res, item.2.as_ref())
71 })
72 } else {
73 self.tree.find_checked(resource, &|idx, res| {
74 let item = &self.resources[idx];
75 check(res, item.2.as_ref())
76 })
77 } {
78 let item = &self.resources[idx];
79 Some((&item.1, ResourceId(item.0.id())))
80 } else {
81 None
82 }
83 }
84
85 pub fn recognize_mut_checked<R, P, F>(
86 &mut self,
87 resource: &mut R,
88 check: F,
89 ) -> Option<(&mut T, ResourceId)>
90 where
91 F: Fn(&R, Option<&U>) -> bool,
92 R: Resource<P>,
93 P: ResourcePath,
94 {
95 if let Some(idx) = if self.insensitive {
96 self.tree.find_checked_insensitive(resource, &|idx, res| {
97 let item = &self.resources[idx];
98 check(res, item.2.as_ref())
99 })
100 } else {
101 self.tree.find_checked(resource, &|idx, res| {
102 let item = &self.resources[idx];
103 check(res, item.2.as_ref())
104 })
105 } {
106 let item = &mut self.resources[idx];
107 Some((&mut item.1, ResourceId(item.0.id())))
108 } else {
109 None
110 }
111 }
112}
113
114#[derive(Debug)]
115pub struct RouterBuilder<T, U = ()> {
116 insensitive: bool,
117 resources: Vec<(ResourceDef, T, Option<U>)>,
118}
119
120impl<T, U> RouterBuilder<T, U> {
121 pub fn case_insensitive(&mut self) {
126 self.insensitive = true;
127 }
128
129 pub fn path<P: IntoPattern>(
131 &mut self,
132 path: P,
133 resource: T,
134 ) -> &mut (ResourceDef, T, Option<U>) {
135 self.resources
136 .push((ResourceDef::new(path), resource, None));
137 self.resources.last_mut().unwrap()
138 }
139
140 pub fn prefix(
142 &mut self,
143 prefix: &str,
144 resource: T,
145 ) -> &mut (ResourceDef, T, Option<U>) {
146 self.resources
147 .push((ResourceDef::prefix(prefix), resource, None));
148 self.resources.last_mut().unwrap()
149 }
150
151 pub fn rdef(
153 &mut self,
154 rdef: ResourceDef,
155 resource: T,
156 ) -> &mut (ResourceDef, T, Option<U>) {
157 self.resources.push((rdef, resource, None));
158 self.resources.last_mut().unwrap()
159 }
160
161 pub fn finish(self) -> Router<T, U> {
163 let tree = if self.resources.is_empty() {
164 Tree::default()
165 } else {
166 let mut tree = Tree::new(&self.resources[0].0, 0);
167 for (idx, r) in self.resources[1..].iter().enumerate() {
168 tree.insert(&r.0, idx + 1)
169 }
170 tree
171 };
172
173 Router {
174 tree,
175 resources: self.resources,
176 insensitive: self.insensitive,
177 }
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use crate::path::Path;
184 use crate::router::{ResourceId, Router};
185
186 #[test]
187 fn test_recognizer_1() {
188 let mut router = Router::<usize>::build();
189 router.path("/name", 10).0.set_id(0);
190 router.path("/name/{val}", 11).0.set_id(1);
191 router.path("/name/{val}/index.html", 12).0.set_id(2);
192 router.path("/file/{file}.{ext}", 13).0.set_id(3);
193 router.path("/v{val}/{val2}/index.html", 14).0.set_id(4);
194 router.path("/v/{tail}*", 15).0.set_id(5);
195 router.path("/test2/{test}.html", 16).0.set_id(6);
196 router.path("/{test}/index.html", 17).0.set_id(7);
197 router.path("/v2/{custom:.*}/test.html", 18).0.set_id(8);
198 let mut router = router.finish();
199
200 let mut path = Path::new("/unknown");
201 assert!(router.recognize_mut(&mut path).is_none());
202
203 let mut path = Path::new("/unknown");
204 assert!(router.recognize(&mut path).is_none());
205
206 let mut path = Path::new("/name");
207 let (h, info) = router.recognize_mut(&mut path).unwrap();
208 assert_eq!(*h, 10);
209 assert_eq!(info, ResourceId(0));
210 assert!(path.is_empty());
211
212 let mut path = Path::new("/name");
213 let (h, info) = router.recognize(&mut path).unwrap();
214 assert_eq!(*h, 10);
215 assert_eq!(info, ResourceId(0));
216 assert!(path.is_empty());
217
218 let mut path = Path::new("/name/value");
219 let (h, info) = router.recognize_mut(&mut path).unwrap();
220 assert_eq!(*h, 11);
221 assert_eq!(info, ResourceId(1));
222 assert_eq!(path.get("val").unwrap(), "value");
223 assert_eq!(&path["val"], "value");
224
225 let mut path = Path::new("/name/value2/index.html");
226 let (h, info) = router.recognize_mut(&mut path).unwrap();
227 assert_eq!(*h, 12);
228 assert_eq!(info, ResourceId(2));
229 assert_eq!(path.get("val").unwrap(), "value2");
230
231 let mut path = Path::new("/file/file.gz");
232 let (h, info) = router.recognize_mut(&mut path).unwrap();
233 assert_eq!(*h, 13);
234 assert_eq!(info, ResourceId(3));
235 assert_eq!(path.get("file").unwrap(), "file");
236 assert_eq!(path.get("ext").unwrap(), "gz");
237
238 let mut path = Path::new("/vtest/ttt/index.html");
239 let (h, info) = router.recognize_mut(&mut path).unwrap();
240 assert_eq!(*h, 14);
241 assert_eq!(info, ResourceId(4));
242 assert_eq!(path.get("val").unwrap(), "test");
243 assert_eq!(path.get("val2").unwrap(), "ttt");
244
245 let mut path = Path::new("/v/blah-blah/index.html");
246 let (h, info) = router.recognize_mut(&mut path).unwrap();
247 assert_eq!(*h, 15);
248 assert_eq!(info, ResourceId(5));
249 assert_eq!(path.get("tail").unwrap(), "blah-blah/index.html");
250
251 let mut path = Path::new("/test2/index.html");
252 let (h, info) = router.recognize_mut(&mut path).unwrap();
253 assert_eq!(*h, 16);
254 assert_eq!(info, ResourceId(6));
255 assert_eq!(path.get("test").unwrap(), "index");
256
257 let mut path = Path::new("/bbb/index.html");
258 let (h, info) = router.recognize_mut(&mut path).unwrap();
259 assert_eq!(*h, 17);
260 assert_eq!(info, ResourceId(7));
261 assert_eq!(path.get("test").unwrap(), "bbb");
262
263 let mut path = Path::new("/v2/blah-blah/test.html");
264 let (h, info) = router.recognize_mut(&mut path).unwrap();
265 assert_eq!(*h, 18);
266 assert_eq!(info, ResourceId(8));
267 assert_eq!(path.get("custom").unwrap(), "blah-blah");
268 }
269
270 #[test]
271 fn test_recognizer_2() {
272 let mut router = Router::<usize>::build();
273 router.path("/index.json", 10);
274 router.path("/{source}.json", 11);
275 let mut router = router.finish();
276
277 let mut path = Path::new("/index.json");
278 let (h, _) = router.recognize_mut(&mut path).unwrap();
279 assert_eq!(*h, 10);
280
281 let mut path = Path::new("/test.json");
282 let (h, _) = router.recognize_mut(&mut path).unwrap();
283 assert_eq!(*h, 11);
284 }
285
286 #[test]
287 fn test_recognizer_3() {
288 let mut router = Router::<usize>::build();
289 router.path("/index.json", 10);
290 router.path("/{source}.json", 11);
291 router.case_insensitive();
292 let mut router = router.finish();
293
294 let mut path = Path::new("/index.json");
295 let (h, _) = router.recognize_mut(&mut path).unwrap();
296 assert_eq!(*h, 10);
297
298 let mut path = Path::new("/indeX.json");
299 let (h, _) = router.recognize_mut(&mut path).unwrap();
300 assert_eq!(*h, 10);
301
302 let mut path = Path::new("/test.jsoN");
303 assert!(router.recognize_mut(&mut path).is_none());
304 }
305
306 #[test]
307 fn test_recognizer_with_path_skip() {
308 let mut router = Router::<usize>::build();
309 router.path("/name", 10).0.set_id(0);
310 router.path("/name/{val}", 11).0.set_id(1);
311 let mut router = router.finish();
312
313 let mut path = Path::new("/name");
314 path.skip(5);
315 assert!(router.recognize_mut(&mut path).is_none());
316
317 let mut path = Path::new("/test/name");
318 path.skip(5);
319 let (h, _) = router.recognize_mut(&mut path).unwrap();
320 assert_eq!(*h, 10);
321
322 let mut path = Path::new("/test/name/value");
323 path.skip(5);
324 let (h, id) = router.recognize_mut(&mut path).unwrap();
325 assert_eq!(*h, 11);
326 assert_eq!(id, ResourceId(1));
327 assert_eq!(path.get("val").unwrap(), "value");
328 assert_eq!(&path["val"], "value");
329
330 let mut router = Router::<usize>::build();
332 router.path("/name", 10);
333 router.path("/name/{val}", 11);
334 let mut router = router.finish();
335
336 let mut path = Path::new("/name");
337 path.skip(5);
338 assert!(router.recognize_mut(&mut path).is_none());
339
340 let mut path = Path::new("/test2/name");
341 path.skip(6);
342 let (h, _) = router.recognize_mut(&mut path).unwrap();
343 assert_eq!(*h, 10);
344
345 let mut path = Path::new("/test2/name-test");
346 path.skip(6);
347 assert!(router.recognize_mut(&mut path).is_none());
348
349 let mut path = Path::new("/test2/name/ttt");
350 path.skip(6);
351 let (h, _) = router.recognize_mut(&mut path).unwrap();
352 assert_eq!(*h, 11);
353 assert_eq!(&path["val"], "ttt");
354 }
355
356 #[test]
357 fn test_recognizer_checked() {
358 let mut router = Router::<usize, usize>::build();
359 router.path("/name", 10).2 = Some(0);
360 router.path("/name", 11).2 = Some(1);
361 router.path("/name", 12).2 = Some(2);
362 let mut router = router.finish();
363
364 let mut p = Path::new("/name");
365 assert_eq!(
366 *router
367 .recognize_checked(&mut p, |_, v| v == Some(&0))
368 .unwrap()
369 .0,
370 10
371 );
372 let mut p = Path::new("/name");
373 assert_eq!(
374 *router
375 .recognize_checked(&mut p, |_, v| v == Some(&1))
376 .unwrap()
377 .0,
378 11
379 );
380 let mut p = Path::new("/name");
381 assert_eq!(
382 *router
383 .recognize_checked(&mut p, |_, v| v == Some(&2))
384 .unwrap()
385 .0,
386 12
387 );
388 let mut p = Path::new("/name");
389 assert_eq!(
390 *router
391 .recognize_mut_checked(&mut p, |_, v| v == Some(&0))
392 .unwrap()
393 .0,
394 10
395 );
396 let mut p = Path::new("/name");
397 assert_eq!(
398 *router
399 .recognize_mut_checked(&mut p, |_, v| v == Some(&1))
400 .unwrap()
401 .0,
402 11
403 );
404 let mut p = Path::new("/name");
405 assert_eq!(
406 *router
407 .recognize_mut_checked(&mut p, |_, v| v == Some(&2))
408 .unwrap()
409 .0,
410 12
411 );
412 }
413
414 #[test]
415 fn test_recognizer_checked_insensitive() {
416 let mut router = Router::<usize, usize>::build();
417 router.case_insensitive();
418 router.path("/name", 10).2 = Some(0);
419 router.path("/name", 11).2 = Some(1);
420 router.path("/name", 12).2 = Some(2);
421 let mut router = router.finish();
422
423 let mut p = Path::new("/Name");
424 assert_eq!(
425 *router
426 .recognize_checked(&mut p, |_, v| v == Some(&0))
427 .unwrap()
428 .0,
429 10
430 );
431 let mut p = Path::new("/Name");
432 assert_eq!(
433 *router
434 .recognize_checked(&mut p, |_, v| v == Some(&1))
435 .unwrap()
436 .0,
437 11
438 );
439 let mut p = Path::new("/Name");
440 assert_eq!(
441 *router
442 .recognize_mut_checked(&mut p, |_, v| v == Some(&0))
443 .unwrap()
444 .0,
445 10
446 );
447 let mut p = Path::new("/name");
448 assert_eq!(
449 *router
450 .recognize_mut_checked(&mut p, |_, v| v == Some(&1))
451 .unwrap()
452 .0,
453 11
454 );
455 }
456}