trek_router/
resource.rs

1use http::Method;
2
3lazy_static! {
4    #[derive(Debug)]
5    pub static ref RESOURCE_ACTIONS: &'static [(&'static str, &'static str, &'static Method)] = &[
6        ("show", "", &Method::GET),
7        ("create", "", &Method::POST),
8        ("update", "", &Method::PATCH),
9        ("update", "", &Method::PUT),
10        ("delete", "", &Method::DELETE),
11        ("edit", "edit", &Method::GET),
12        ("new", "new", &Method::GET),
13    ];
14    pub static ref RESOURCES_ACTIONS: &'static [(&'static str, &'static str, &'static Method)] = &[
15        ("index", "", &Method::GET),
16        ("create", "", &Method::POST),
17        ("new", "new", &Method::GET),
18        ("show", ":id", &Method::GET),
19        ("update", ":id", &Method::PATCH),
20        ("update", ":id", &Method::PUT),
21        ("delete", ":id", &Method::DELETE),
22        ("edit", ":id/edit", &Method::GET)
23    ];
24}
25
26#[derive(Default)]
27pub struct ResourceOptions {
28    only: Vec<&'static str>,
29    except: Vec<&'static str>,
30}
31
32impl ResourceOptions {
33    pub fn only(only: Vec<&'static str>) -> Self {
34        ResourceOptions {
35            only,
36            except: vec![],
37        }
38    }
39
40    pub fn except(except: Vec<&'static str>) -> Self {
41        ResourceOptions {
42            only: vec![],
43            except,
44        }
45    }
46}
47
48pub trait Resource {
49    type Context;
50    type Body;
51
52    fn show(ctx: Self::Context) -> Self::Body;
53    fn create(ctx: Self::Context) -> Self::Body;
54    fn update(ctx: Self::Context) -> Self::Body;
55    fn delete(ctx: Self::Context) -> Self::Body;
56    fn edit(ctx: Self::Context) -> Self::Body;
57    fn new(ctx: Self::Context) -> Self::Body;
58
59    fn build<'a>(
60        opts: ResourceOptions,
61    ) -> Vec<(
62        (&'a str, &'a str, &'a Method),
63        fn(Self::Context) -> Self::Body,
64    )> {
65        let ResourceOptions { only, except } = opts;
66        let mut ra: Vec<_> = RESOURCE_ACTIONS.to_vec();
67        if !only.is_empty() {
68            ra.retain(|t| !only.contains(&t.0));
69        }
70        if !except.is_empty() {
71            ra.retain(|t| except.contains(&t.0));
72        }
73        let mut r: Vec<(
74            (&'a str, &'a str, &'a Method),
75            fn(Self::Context) -> Self::Body,
76        )> = Vec::new();
77        for t in ra {
78            match t.0 {
79                "show" => r.push((t, Self::show)),
80                "create" => r.push((t, Self::create)),
81                "update" => r.push((t, Self::update)),
82                "delete" => r.push((t, Self::delete)),
83                "edit" => r.push((t, Self::edit)),
84                "new" => r.push((t, Self::new)),
85                _ => unimplemented!(),
86            }
87        }
88        r
89    }
90}
91
92pub trait Resources {
93    type Context;
94    type Body;
95
96    fn index(ctx: Self::Context) -> Self::Body;
97    fn create(ctx: Self::Context) -> Self::Body;
98    fn new(ctx: Self::Context) -> Self::Body;
99    fn show(ctx: Self::Context) -> Self::Body;
100    fn update(ctx: Self::Context) -> Self::Body;
101    fn delete(ctx: Self::Context) -> Self::Body;
102    fn edit(ctx: Self::Context) -> Self::Body;
103
104    fn build<'a>(
105        opts: ResourceOptions,
106    ) -> Vec<(
107        (&'a str, &'a str, &'a Method),
108        fn(Self::Context) -> Self::Body,
109    )> {
110        let ResourceOptions { only, except } = opts;
111        let mut ra: Vec<_> = RESOURCES_ACTIONS.to_vec();
112        if !only.is_empty() {
113            ra.retain(|t| !only.contains(&t.0));
114        }
115        if !except.is_empty() {
116            ra.retain(|t| except.contains(&t.0));
117        }
118        let mut r: Vec<(
119            (&'a str, &'a str, &'a Method),
120            fn(Self::Context) -> Self::Body,
121        )> = Vec::new();
122        for t in ra {
123            match t.0 {
124                "index" => r.push((t, Self::index)),
125                "create" => r.push((t, Self::create)),
126                "new" => r.push((t, Self::new)),
127                "show" => r.push((t, Self::show)),
128                "update" => r.push((t, Self::update)),
129                "delete" => r.push((t, Self::delete)),
130                "edit" => r.push((t, Self::edit)),
131                _ => unimplemented!(),
132            }
133        }
134        r
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141    use crate::Router;
142
143    #[test]
144    fn resource() {
145        struct Context {
146            count: usize,
147        }
148        type F = fn(Context) -> usize;
149        let mut router = Router::<F>::new();
150
151        struct Geocoder {}
152
153        impl Resource for Geocoder {
154            type Context = Context;
155            type Body = usize;
156
157            fn show(ctx: Self::Context) -> Self::Body {
158                println!("{}", "Resource Show");
159                ctx.count + 0
160            }
161
162            fn create(ctx: Self::Context) -> Self::Body {
163                println!("{}", "Resource Create");
164                ctx.count + 1
165            }
166
167            fn update(ctx: Self::Context) -> Self::Body {
168                println!("{}", "Resource Update");
169                ctx.count + 2
170            }
171
172            fn delete(ctx: Self::Context) -> Self::Body {
173                println!("{}", "Resource Delete");
174                ctx.count + 3
175            }
176
177            fn edit(ctx: Self::Context) -> Self::Body {
178                println!("{}", "Resource Edit");
179                ctx.count + 4
180            }
181
182            fn new(ctx: Self::Context) -> Self::Body {
183                println!("{}", "Resource New");
184                ctx.count + 5
185            }
186        }
187
188        router.resource("/geocoder", Geocoder::build(ResourceOptions::default()));
189
190        struct Users {}
191
192        impl Resources for Users {
193            type Context = Context;
194            type Body = usize;
195
196            fn index(ctx: Self::Context) -> Self::Body {
197                println!("{}", "Resources Index");
198                ctx.count + 0
199            }
200
201            fn create(ctx: Self::Context) -> Self::Body {
202                println!("{}", "Resources Create");
203                ctx.count + 1
204            }
205
206            fn new(ctx: Self::Context) -> Self::Body {
207                println!("{}", "Resources New");
208                ctx.count + 2
209            }
210
211            fn show(ctx: Self::Context) -> Self::Body {
212                println!("{}", "Resources Show");
213                ctx.count + 3
214            }
215
216            fn update(ctx: Self::Context) -> Self::Body {
217                println!("{}", "Resources Update");
218                ctx.count + 4
219            }
220
221            fn delete(ctx: Self::Context) -> Self::Body {
222                println!("{}", "Resources Delete");
223                ctx.count + 5
224            }
225
226            fn edit(ctx: Self::Context) -> Self::Body {
227                println!("{}", "Resources Edit");
228                ctx.count + 6
229            }
230        }
231
232        router.resources("/users", Users::build(ResourceOptions::default()));
233
234        dbg!(&router);
235
236        let ctx = Context { count: 0 };
237        let r = router.find(&Method::GET, "/geocoder");
238        assert!(r.is_some());
239        let (h, p) = r.unwrap();
240        assert_eq!(h(ctx), 0);
241        assert_eq!(p, []);
242
243        let ctx = Context { count: 0 };
244        let r = router.find(&Method::POST, "/geocoder");
245        assert!(r.is_some());
246        let (h, p) = r.unwrap();
247        assert_eq!(h(ctx), 1);
248        assert_eq!(p, []);
249
250        let ctx = Context { count: 0 };
251        let r = router.find(&Method::PATCH, "/geocoder");
252        assert!(r.is_some());
253        let (h, p) = r.unwrap();
254        assert_eq!(h(ctx), 2);
255        assert_eq!(p, []);
256
257        let ctx = Context { count: 0 };
258        let r = router.find(&Method::PUT, "/geocoder");
259        assert!(r.is_some());
260        let (h, p) = r.unwrap();
261        assert_eq!(h(ctx), 2);
262        assert_eq!(p, []);
263
264        let ctx = Context { count: 0 };
265        let r = router.find(&Method::DELETE, "/geocoder");
266        assert!(r.is_some());
267        let (h, p) = r.unwrap();
268        assert_eq!(h(ctx), 3);
269        assert_eq!(p, []);
270
271        let ctx = Context { count: 0 };
272        let r = router.find(&Method::GET, "/geocoder/edit");
273        assert!(r.is_some());
274        let (h, p) = r.unwrap();
275        assert_eq!(h(ctx), 4);
276        assert_eq!(p, []);
277
278        let ctx = Context { count: 0 };
279        let r = router.find(&Method::GET, "/geocoder/new");
280        assert!(r.is_some());
281        let (h, p) = r.unwrap();
282        assert_eq!(h((ctx)), 5);
283        assert_eq!(p, []);
284
285        let ctx = Context { count: 0 };
286        let r = router.find(&Method::GET, "/users");
287        assert!(r.is_some());
288        let (h, p) = r.unwrap();
289        assert_eq!(h((ctx)), 0);
290        assert_eq!(p, []);
291
292        let ctx = Context { count: 0 };
293        let r = router.find(&Method::POST, "/users");
294        assert!(r.is_some());
295        let (h, p) = r.unwrap();
296        assert_eq!(h((ctx)), 1);
297        assert_eq!(p, []);
298
299        let ctx = Context { count: 0 };
300        let r = router.find(&Method::GET, "/users/new");
301        assert!(r.is_some());
302        let (h, p) = r.unwrap();
303        assert_eq!(h((ctx)), 2);
304        assert_eq!(p, []);
305
306        let ctx = Context { count: 0 };
307        let r = router.find(&Method::GET, "/users/1");
308        assert!(r.is_some());
309        let (h, p) = r.unwrap();
310        assert_eq!(h((ctx)), 3);
311        assert_eq!(p, [("user_id", "1")]);
312
313        let ctx = Context { count: 0 };
314        let r = router.find(&Method::PATCH, "/users/1");
315        assert!(r.is_some());
316        let (h, p) = r.unwrap();
317        assert_eq!(h((ctx)), 4);
318        assert_eq!(p, [("user_id", "1")]);
319
320        let ctx = Context { count: 0 };
321        let r = router.find(&Method::PUT, "/users/1");
322        assert!(r.is_some());
323        let (h, p) = r.unwrap();
324        assert_eq!(h((ctx)), 4);
325        assert_eq!(p, [("user_id", "1")]);
326
327        let ctx = Context { count: 0 };
328        let r = router.find(&Method::DELETE, "/users/1");
329        assert!(r.is_some());
330        let (h, p) = r.unwrap();
331        assert_eq!(h((ctx)), 5);
332        assert_eq!(p, [("user_id", "1")]);
333
334        let ctx = Context { count: 0 };
335        let r = router.find(&Method::GET, "/users/1/edit");
336        assert!(r.is_some());
337        let (h, p) = r.unwrap();
338        assert_eq!(h((ctx)), 6);
339        assert_eq!(p, [("user_id", "1")]);
340    }
341}