1use std::fmt;
2use std::marker::PhantomData;
3use std::str::FromStr;
4use std::error::Error;
5use std::convert::TryFrom;
6
7use http::Method;
8use plumb::tuple_utils::Append;
9
10use crate::tuple_macro;
11
12#[macro_export]
13macro_rules! route {
14 (* / $($pieces:tt)*) => {
15 $crate::route_impl!(
16 $crate::route::RouteBuilder::new(None);
17 $($pieces)*
18 )
19 };
20 ($method:ident / $($pieces:tt)*) => {
21 $crate::route_impl!(
22 $crate::route::RouteBuilder::new(Some(::http::Method::$method));
23 $($pieces)*
24 )
25 };
26}
27
28#[doc(hidden)]
29#[macro_export]
30macro_rules! route_impl {
31 ($acc:expr;) => {
32 $acc.end()
33 };
34 ($acc:expr; [ .. ] []) => {
35 $acc.rest()
36 };
37 ($acc:expr; [ $cur:literal ] []) => {
38 $crate::route_impl!(
39 $acc.exact($cur.to_string());
40 )
41 };
42 ($acc:expr; [ $cur:literal ] [ / $head:tt $(/ $tail:tt)* ]) => {
43 $crate::route_impl!(
44 $acc.exact($cur.to_string());
45 [ $head ]
46 [ $(/ $tail)* ]
47 )
48 };
49 ($acc:expr; [ $cur:ty ] []) => {
50 $crate::route_impl!(
51 $acc.var::<$cur>();
52 )
53 };
54 ($acc:expr; [ $cur:ty ] [ / $head:tt $(/ $tail:tt)* ]) => {
55 $crate::route_impl!(
56 $acc.var::<$cur>();
57 [ $head ]
58 [ $(/ $tail)* ]
59 )
60 };
61 ($acc:expr; $head:tt $(/ $tail:tt)*) => {
62 $crate::route_impl!(
63 $acc;
64 [$head]
65 [$(/ $tail)*]
66 )
67 };
68}
69
70fn _test() {
71 let _p : Route<()> = route!(* /);
72 let _p : Route<()> = route!(* / "hello");
73 let _p : Route<()> = route!(GET / "hello" / "world");
74 let _p : Route<(u32,)> = route!(* / u32);
75 let _p : Route<(u32,String)> = route!(POST / u32 / ..);
76 let _p : Route<(u32,String)> = route!(* / "hello" / u32 / ..);
77}
78
79#[derive(Debug,Clone,PartialEq,Eq)]
80enum PathToken {
81 Exact(String),
83 Var,
84 Rest,
85 End,
86}
87
88
89
90fn parse_path_tokens<'a>(
91 tokens : &[PathToken],
92 path : &'a str,
93 vars : &mut Vec<&'a str>,
94) -> Result<Option<&'a str>, ()> {
95
96 vars.clear();
97
98 fn next_segment<'a>(p : &'a str) -> (&'a str, &'a str) {
99 assert!(p.len() > 0 && &p[0..1] == "/");
100 let p = p.split_at(1).1;
101 match p.find('/') {
102 Some(n) => p.split_at(n),
103 None => (p, ""),
104 }
105 }
106
107 fn scan<'a>(
108 tok : &PathToken,
109 s : &'a str,
110 vars : &mut Vec<&'a str>
111 ) -> Result<Option<&'a str>, ()> {
112 assert!(&s[0..1] == "/");
113
114 use PathToken::*;
115
116 match tok {
117 Var => {
118 let (seg, rest) = next_segment(&s);
119 vars.push(seg);
120 if rest.is_empty() {
121 Ok(None)
122 } else {
123 Ok(Some(rest))
124 }
125 },
126 Exact(exact) => {
127 let (seg, rest) = next_segment(&s);
128 if &seg != &exact {
129 return Err(())
130 } else if rest.is_empty() {
131 Ok(None)
132 } else {
133 Ok(Some(rest))
134 }
135 },
136 Rest => {
137 vars.push(s);
138 Ok(None)
139 },
140 End => {
141 if s.is_empty() || s == "/" {
142 Ok(None)
143 } else {
144 Err(())
145 }
146 }
147 }
148 }
149
150 let mut cursor = path;
151 let mut it = tokens.iter();
152
153 while let Some(token) = it.next() {
154 match scan(token, cursor, vars) {
155 Ok(Some(next)) => cursor = next,
156 Ok(None) => {
157 return match it.next() {
158 Some(PathToken::End) | None => Ok(None),
159 _ => Err(()),
160 }
161 }
162 Err(_) => return Err(()),
163 }
164 }
165
166 return Ok(Some(cursor))
167}
168
169
170pub struct RouteBuilder<T> {
171 method : Option<Method>,
172 tokens : Vec<PathToken>,
173 _marker : PhantomData<fn () -> T>
174}
175
176impl RouteBuilder<()> {
177 pub fn new(method : Option<Method>) -> Self {
178 RouteBuilder {
179 method,
180 tokens : Vec::new(),
181 _marker : Default::default(),
182 }
183 }
184}
185
186impl<T> RouteBuilder<T> {
187 pub fn var<U : FromStr>(mut self) -> RouteBuilder<T::Output>
188 where
189 T : Append<U>
190 {
191 self.tokens.push(PathToken::Var);
192
193 RouteBuilder::<T::Output> {
194 method : self.method,
195 tokens : self.tokens,
196 _marker : Default::default(),
197 }
198 }
199
200 pub fn exact(mut self, s : String) -> Self {
201 self.tokens.push(PathToken::Exact(s));
202 self
203 }
204
205 pub fn end(mut self) -> Route<T> {
206 self.tokens.push(PathToken::End);
207 self.build()
208 }
209
210 pub fn rest(mut self) -> Route<T::Output>
211 where
212 T : Append<String>
213 {
214 self.tokens.push(PathToken::Rest);
215 RouteBuilder::<T::Output> {
216 method : self.method,
217 tokens : self.tokens,
218 _marker : Default::default(),
219 }.build()
220 }
221
222 fn build(self) -> Route<T> {
223 Route{
224 method : self.method,
225 tokens : self.tokens,
226 _marker : self._marker,
227 }
228 }
229}
230
231
232#[derive(Debug)]
233pub(crate) struct RouteUntyped {
234 method : Option<Method>,
235 tokens : Vec<PathToken>,
236}
237
238impl<T> From<Route<T>> for RouteUntyped {
239 fn from(r : Route<T>) -> Self {
240 Self{
241 method: r.method,
242 tokens: r.tokens,
243 }
244 }
245}
246
247impl RouteUntyped {
248 pub(crate) fn path_matches<B>(&self, req : &http::Request<B>) -> bool {
249 parse_path_tokens(&self.tokens, req.uri().path(), &mut Vec::new()).is_ok()
250 }
251
252 pub(crate) fn method_matches<B>(&self, req : &http::Request<B>) -> bool {
253 self.method.is_none() || self.method.as_ref() == Some(req.method())
254 }
255}
256
257pub struct Route<T> {
258 method : Option<Method>,
259 tokens : Vec<PathToken>,
260 _marker : PhantomData<fn () -> T>
261}
262
263impl<T> Clone for Route<T> {
264 fn clone(&self) -> Self {
265 Self{
266 method : self.method.clone(),
267 tokens : self.tokens.clone(),
268 _marker : Default::default(),
269 }
270 }
271}
272
273#[derive(Debug)]
274pub enum PathParseError {
275 NoMatch,
276 VarCountMismatch{
277 tuple_size : usize,
278 var_count : usize,
279 },
280 FromStr(Box<dyn Error + Send + Sync>),
281}
282
283impl fmt::Display for PathParseError {
284 fn fmt(&self, f : &mut fmt::Formatter) -> Result<(), fmt::Error> {
285 use PathParseError::*;
286 match self {
287 NoMatch => write!(f, "not a match"),
288 VarCountMismatch{tuple_size, var_count} => write!(
289 f,
290 "tuple has size {} but path has {} variables",
291 tuple_size,
292 var_count,
293 ),
294 FromStr(e) => e.fmt(f)
295 }
296 }
297}
298
299impl<T> Route<T> {
300
301 pub fn parser<'a, 'b>(&'a self, s : &'b str) -> PathParser<'a, 'b> {
302 PathParser{
303 tokens : &self.tokens,
304 path : s,
305 }
306 }
307}
308
309
310
311macro_rules! toss {
312 ($x:tt) => { () }
313}
314
315macro_rules! impl_from_path_parser {
316 ($($x:ident,)*) => {
317
318 impl<$($x,)*> TryFrom<PathParser<'_, '_>> for ($($x,)*)
319 where
320 $(
321 $x : FromStr,
322 $x::Err : Error + Send + Sync + 'static,
323 )*
324 {
325 type Error = PathParseError; fn try_from(mut it : PathParser) -> Result<Self, Self::Error> {
328 #![allow(non_snake_case,unused_variables,unused_mut)]
329
330 let units : &[()] = &[
331 $(
332 toss!($x),
333 )*
334 ];
335
336 let tuple_size = units.len();
337 let mut n = 0;
338
339 $(
340 let v = it.next().ok_or_else(|| {
341 PathParseError::VarCountMismatch{
342 tuple_size,
343 var_count : n,
344 }
345 })?.map_err(|()| {
346 PathParseError::NoMatch
347 })?;
348
349 let $x = $x::from_str(v)
350 .map_err(|x| PathParseError::FromStr(Box::new(x)))?;
351
352 n += 1;
353 )*
354
355 if it.next().is_some() {
356 return Err(PathParseError::VarCountMismatch{
357 tuple_size,
358 var_count : n + 1,
359 })
360 }
361
362 Ok((
363 $(
364 $x,
365 )*
366 ))
367 }
368 }
369 }
370}
371
372tuple_macro!{impl_from_path_parser}
373
374#[cfg(test)]
375mod test {
376 use super::*;
377
378 #[test]
379 fn test1() {
380 use PathToken::*;
381
382 let res = RouteBuilder::new(None)
383 .exact("a".to_string())
384 .build()
385 .parse("/a");
386
387 assert!(matches!(
388 res,
389 Ok(((), None))
390 ));
391
392 let res = RouteBuilder::new(None)
393 .exact("a".to_string())
394 .var::<u8>()
395 .build()
396 .parse("/a/10");
397
398 assert!(matches!(
399 res,
400 Ok(((10,), None))
401 ));
402
403 let res = RouteBuilder::new(None)
404 .exact("a".to_string())
405 .var::<u8>()
406 .build()
407 .parse("/a/256");
408
409 assert!(matches!(
410 res,
411 Err(_),
412 ));
413
414
415 let res = RouteBuilder::new(None)
416 .exact("b".to_string())
417 .build()
418 .parse("/a");
419
420 assert!(matches!(res, Err(PathParseError::NoMatch)));
421
422 let res = RouteBuilder::new(None)
423 .exact("a".to_string())
424 .rest()
425 .parse("/a/");
426
427 assert!(matches!(
428 res,
429 Ok(((s,), None)) if s == "/"
430 ));
431
432 let res = RouteBuilder::new(None)
433 .exact("a".to_string())
434 .rest()
435 .parse("/a");
436
437 assert!(matches!(
438 res,
439 Err(PathParseError::NoMatch),
440 ));
441
442
443 let res = RouteBuilder::new(None)
444 .var::<String>()
445 .build()
446 .parse("/a");
447
448 assert!(matches!(
449 res,
450 Ok(((a,), None)) if a == "a"
451 ));
452
453
454 let res = RouteBuilder::new(None)
455 .rest()
456 .parse("/");
457
458 assert!(matches!(
459 res,
460 Ok(((a,), None)) if a == "/"
461 ));
462
463
464 let res = RouteBuilder::new(None)
465 .var::<String>()
466 .exact("b".to_string())
467 .rest()
468 .parse("/a/b/c/d");
469
470 assert!(matches!(
471 res,
472 Ok(((a,cd), None)) if a == "a" && cd == "/c/d"
473 ));
474
475 let res = RouteBuilder::new(None)
476 .var::<String>()
477 .exact("b".to_string())
478 .var::<String>()
479 .end()
480 .parse("/a/b/c");
481
482 assert!(matches!(
483 res,
484 Ok(((a,c), None)) if a == "a" && c == "c"
485 ));
486 }
487}
488
489pub struct PathParser<'a, 'b> {
490 tokens : &'a [PathToken],
491 path : &'b str
492}
493
494impl<'a, 'b> PathParser<'a, 'b> {
495 pub fn rest(&self) -> &'b str {
496 self.path
497 }
498}
499
500impl<'a, 'b> Iterator for PathParser<'a, 'b> {
501 type Item = Result<&'b str, ()>;
502
503 fn next(&mut self) -> Option<Self::Item> {
504 if self.tokens.len() == 0 {
505 return None
506 }
507
508 if self.path.len() == 0 {
509 return match self.tokens.get(0) {
510 Some(PathToken::End) | None => None,
511 _ => Some(Err(())),
512 }
513 }
514
515 let mut var = None;
516
517 loop {
518 let cur = &self.tokens[0];
519 self.tokens = &self.tokens[1..];
520
521 match scan(cur, self.path, &mut var) {
522 Ok(Some(next)) => {
523 self.path = next;
524
525 if var.is_some() {
526 return var.map(Ok)
527 }
528 }
529 Ok(None) => {
530 let (_, end) = self.path.split_at(self.path.len());
531 self.path = end;
532
533 if var.is_some() {
534 return var.map(Ok)
535 }
536
537 return match self.tokens.get(0) {
538 Some(PathToken::End) | None => None,
539 _ => Some(Err(())),
540 }
541 },
542 Err(_) => return Some(Err(()))
543 }
544
545 }
546 }
547}
548
549fn next_segment<'a>(p : &'a str) -> (&'a str, &'a str) {
550 assert!(p.len() > 0 && &p[0..1] == "/");
551 let p = p.split_at(1).1;
552 match p.find('/') {
553 Some(n) => p.split_at(n),
554 None => (p, ""),
555 }
556}
557
558fn scan<'a>(
559 tok : &PathToken,
560 s : &'a str,
561 var : &mut Option<&'a str>
562) -> Result<Option<&'a str>, ()> {
563 assert!(&s[0..1] == "/");
564
565 use PathToken::*;
566
567 match tok {
568 Var => {
569 let (seg, rest) = next_segment(&s);
570 *var = Some(seg);
571 if rest.is_empty() {
572 Ok(None)
573 } else {
574 Ok(Some(rest))
575 }
576 },
577 Exact(exact) => {
578 let (seg, rest) = next_segment(&s);
579 if &seg != &exact {
580 return Err(())
581 } else if rest.is_empty() {
582 Ok(None)
583 } else {
584 Ok(Some(rest))
585 }
586 },
587 Rest => {
588 *var = Some(s);
589 Ok(None)
590 },
591 End => {
592 if s.is_empty() || s == "/" {
593 Ok(None)
594 } else {
595 Err(())
596 }
597 }
598 }
599}
600
601#[cfg(test)]
602#[test]
603fn test() {
604 let r = route!(* / "hello").into();
605 let mut it = PathParser::new(
606 &r,
607 "/hello"
608 );
609
610 assert!(it.next() == None);
611 assert_eq!(it.rest(), "");
612
613 let r = route!(* / "hello" / u32).into();
614 let mut it = PathParser::new(
615 &r,
616 "/hello/10"
617 );
618
619 assert_eq!(it.next(), Some(Ok("10")));
620 assert_eq!(it.next(), None);
621 assert_eq!(it.path, "");
622
623 let r = route!(* / "hello" / u32 / "goodbye").into();
624 let mut it = PathParser::new(
625 &r,
626 "/hello/10/goodbye"
627 );
628
629 assert_eq!(it.next(), Some(Ok("10")));
630 assert_eq!(it.next(), None);
631 assert_eq!(it.path, "");
632
633 let r = route!(* / "hello" / u32 / .. ).into();
634 let mut it = PathParser::new(
635 &r,
636 "/hello/10/goodbye/true"
637 );
638
639 assert_eq!(it.next(), Some(Ok("10")));
640 assert_eq!(it.next(), Some(Ok("/goodbye/true")));
641 assert_eq!(it.path, "");
642}
643