http_mux/
route.rs

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 matches should have highest priority
82    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; // Box<dyn Error + Send + Sync>;
326
327            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