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}