scrappy_router/
router.rs

1use crate::{IntoPattern, Resource, ResourceDef, ResourcePath};
2
3#[derive(Debug, Copy, Clone, PartialEq)]
4pub struct ResourceId(pub u16);
5
6/// Information about current resource
7#[derive(Clone, Debug)]
8pub struct ResourceInfo {
9    resource: ResourceId,
10}
11
12/// Resource router.
13pub struct Router<T, U = ()>(Vec<(ResourceDef, T, Option<U>)>);
14
15impl<T, U> Router<T, U> {
16    pub fn build() -> RouterBuilder<T, U> {
17        RouterBuilder {
18            resources: Vec::new(),
19        }
20    }
21
22    pub fn recognize<R, P>(&self, resource: &mut R) -> Option<(&T, ResourceId)>
23    where
24        R: Resource<P>,
25        P: ResourcePath,
26    {
27        for item in self.0.iter() {
28            if item.0.match_path(resource.resource_path()) {
29                return Some((&item.1, ResourceId(item.0.id())));
30            }
31        }
32        None
33    }
34
35    pub fn recognize_mut<R, P>(&mut self, resource: &mut R) -> Option<(&mut T, ResourceId)>
36    where
37        R: Resource<P>,
38        P: ResourcePath,
39    {
40        for item in self.0.iter_mut() {
41            if item.0.match_path(resource.resource_path()) {
42                return Some((&mut item.1, ResourceId(item.0.id())));
43            }
44        }
45        None
46    }
47
48    pub fn recognize_mut_checked<R, P, F>(
49        &mut self,
50        resource: &mut R,
51        check: F,
52    ) -> Option<(&mut T, ResourceId)>
53    where
54        F: Fn(&R, &Option<U>) -> bool,
55        R: Resource<P>,
56        P: ResourcePath,
57    {
58        for item in self.0.iter_mut() {
59            if item.0.match_path_checked(resource, &check, &item.2) {
60                return Some((&mut item.1, ResourceId(item.0.id())));
61            }
62        }
63        None
64    }
65}
66
67pub struct RouterBuilder<T, U = ()> {
68    resources: Vec<(ResourceDef, T, Option<U>)>,
69}
70
71impl<T, U> RouterBuilder<T, U> {
72    /// Register resource for specified path.
73    pub fn path<P: IntoPattern>(
74        &mut self,
75        path: P,
76        resource: T,
77    ) -> &mut (ResourceDef, T, Option<U>) {
78        self.resources
79            .push((ResourceDef::new(path), resource, None));
80        self.resources.last_mut().unwrap()
81    }
82
83    /// Register resource for specified path prefix.
84    pub fn prefix(&mut self, prefix: &str, resource: T) -> &mut (ResourceDef, T, Option<U>) {
85        self.resources
86            .push((ResourceDef::prefix(prefix), resource, None));
87        self.resources.last_mut().unwrap()
88    }
89
90    /// Register resource for ResourceDef
91    pub fn rdef(&mut self, rdef: ResourceDef, resource: T) -> &mut (ResourceDef, T, Option<U>) {
92        self.resources.push((rdef, resource, None));
93        self.resources.last_mut().unwrap()
94    }
95
96    /// Finish configuration and create router instance.
97    pub fn finish(self) -> Router<T, U> {
98        Router(self.resources)
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use crate::path::Path;
105    use crate::router::{ResourceId, Router};
106
107    #[test]
108    fn test_recognizer_1() {
109        let mut router = Router::<usize>::build();
110        router.path("/name", 10).0.set_id(0);
111        router.path("/name/{val}", 11).0.set_id(1);
112        router.path("/name/{val}/index.html", 12).0.set_id(2);
113        router.path("/file/{file}.{ext}", 13).0.set_id(3);
114        router.path("/v{val}/{val2}/index.html", 14).0.set_id(4);
115        router.path("/v/{tail:.*}", 15).0.set_id(5);
116        router.path("/test2/{test}.html", 16).0.set_id(6);
117        router.path("/{test}/index.html", 17).0.set_id(7);
118        let mut router = router.finish();
119
120        let mut path = Path::new("/unknown");
121        assert!(router.recognize_mut(&mut path).is_none());
122
123        let mut path = Path::new("/name");
124        let (h, info) = router.recognize_mut(&mut path).unwrap();
125        assert_eq!(*h, 10);
126        assert_eq!(info, ResourceId(0));
127        assert!(path.is_empty());
128
129        let mut path = Path::new("/name/value");
130        let (h, info) = router.recognize_mut(&mut path).unwrap();
131        assert_eq!(*h, 11);
132        assert_eq!(info, ResourceId(1));
133        assert_eq!(path.get("val").unwrap(), "value");
134        assert_eq!(&path["val"], "value");
135
136        let mut path = Path::new("/name/value2/index.html");
137        let (h, info) = router.recognize_mut(&mut path).unwrap();
138        assert_eq!(*h, 12);
139        assert_eq!(info, ResourceId(2));
140        assert_eq!(path.get("val").unwrap(), "value2");
141
142        let mut path = Path::new("/file/file.gz");
143        let (h, info) = router.recognize_mut(&mut path).unwrap();
144        assert_eq!(*h, 13);
145        assert_eq!(info, ResourceId(3));
146        assert_eq!(path.get("file").unwrap(), "file");
147        assert_eq!(path.get("ext").unwrap(), "gz");
148
149        let mut path = Path::new("/vtest/ttt/index.html");
150        let (h, info) = router.recognize_mut(&mut path).unwrap();
151        assert_eq!(*h, 14);
152        assert_eq!(info, ResourceId(4));
153        assert_eq!(path.get("val").unwrap(), "test");
154        assert_eq!(path.get("val2").unwrap(), "ttt");
155
156        let mut path = Path::new("/v/blah-blah/index.html");
157        let (h, info) = router.recognize_mut(&mut path).unwrap();
158        assert_eq!(*h, 15);
159        assert_eq!(info, ResourceId(5));
160        assert_eq!(path.get("tail").unwrap(), "blah-blah/index.html");
161
162        let mut path = Path::new("/test2/index.html");
163        let (h, info) = router.recognize_mut(&mut path).unwrap();
164        assert_eq!(*h, 16);
165        assert_eq!(info, ResourceId(6));
166        assert_eq!(path.get("test").unwrap(), "index");
167
168        let mut path = Path::new("/bbb/index.html");
169        let (h, info) = router.recognize_mut(&mut path).unwrap();
170        assert_eq!(*h, 17);
171        assert_eq!(info, ResourceId(7));
172        assert_eq!(path.get("test").unwrap(), "bbb");
173    }
174
175    #[test]
176    fn test_recognizer_2() {
177        let mut router = Router::<usize>::build();
178        router.path("/index.json", 10);
179        router.path("/{source}.json", 11);
180        let mut router = router.finish();
181
182        let mut path = Path::new("/index.json");
183        let (h, _) = router.recognize_mut(&mut path).unwrap();
184        assert_eq!(*h, 10);
185
186        let mut path = Path::new("/test.json");
187        let (h, _) = router.recognize_mut(&mut path).unwrap();
188        assert_eq!(*h, 11);
189    }
190
191    #[test]
192    fn test_recognizer_with_prefix() {
193        let mut router = Router::<usize>::build();
194        router.path("/name", 10).0.set_id(0);
195        router.path("/name/{val}", 11).0.set_id(1);
196        let mut router = router.finish();
197
198        let mut path = Path::new("/name");
199        path.skip(5);
200        assert!(router.recognize_mut(&mut path).is_none());
201
202        let mut path = Path::new("/test/name");
203        path.skip(5);
204        let (h, _) = router.recognize_mut(&mut path).unwrap();
205        assert_eq!(*h, 10);
206
207        let mut path = Path::new("/test/name/value");
208        path.skip(5);
209        let (h, id) = router.recognize_mut(&mut path).unwrap();
210        assert_eq!(*h, 11);
211        assert_eq!(id, ResourceId(1));
212        assert_eq!(path.get("val").unwrap(), "value");
213        assert_eq!(&path["val"], "value");
214
215        // same patterns
216        let mut router = Router::<usize>::build();
217        router.path("/name", 10);
218        router.path("/name/{val}", 11);
219        let mut router = router.finish();
220
221        let mut path = Path::new("/name");
222        path.skip(6);
223        assert!(router.recognize_mut(&mut path).is_none());
224
225        let mut path = Path::new("/test2/name");
226        path.skip(6);
227        let (h, _) = router.recognize_mut(&mut path).unwrap();
228        assert_eq!(*h, 10);
229
230        let mut path = Path::new("/test2/name-test");
231        path.skip(6);
232        assert!(router.recognize_mut(&mut path).is_none());
233
234        let mut path = Path::new("/test2/name/ttt");
235        path.skip(6);
236        let (h, _) = router.recognize_mut(&mut path).unwrap();
237        assert_eq!(*h, 11);
238        assert_eq!(&path["val"], "ttt");
239    }
240}