2021-06-06 15:19:54 +02:00
use crate ::{ body ::BoxBody , response ::IntoResponse , ResultExt };
2021-05-30 13:24:03 +02:00
use bytes ::Bytes ;
use futures_util ::{ future , ready };
2021-06-06 20:30:54 +02:00
use http ::{ Method , Request , Response , StatusCode , Uri };
use http_body ::Full ;
2021-06-04 01:00:48 +02:00
use hyper ::Body ;
use itertools ::Itertools ;
2021-05-30 13:24:03 +02:00
use pin_project ::pin_project ;
2021-06-04 01:00:48 +02:00
use regex ::Regex ;
2021-05-30 13:24:03 +02:00
use std ::{
2021-06-04 01:00:48 +02:00
borrow ::Cow ,
2021-05-30 13:24:03 +02:00
convert ::Infallible ,
future ::Future ,
pin ::Pin ,
2021-06-04 01:00:48 +02:00
sync ::Arc ,
2021-05-30 13:24:03 +02:00
task ::{ Context , Poll },
};
2021-05-31 16:28:26 +02:00
use tower ::{
2021-06-04 01:00:48 +02:00
buffer ::Buffer ,
util ::{ BoxService , Oneshot , ServiceExt },
2021-06-01 21:15:48 +02:00
BoxError , Layer , Service , ServiceBuilder ,
2021-05-31 16:28:26 +02:00
};
2021-05-30 13:24:03 +02:00
2021-06-04 01:00:48 +02:00
// ===== DSL =====
2021-05-30 13:24:03 +02:00
2021-06-06 11:37:08 +02:00
#[derive(Debug, Copy, Clone)]
pub enum MethodFilter {
Any ,
Connect ,
Delete ,
Get ,
Head ,
Options ,
Patch ,
Post ,
Put ,
Trace ,
}
impl MethodFilter {
#[allow(clippy::match_like_matches_macro)]
2021-06-06 15:19:54 +02:00
pub ( crate ) fn matches ( self , method : & Method ) -> bool {
2021-06-06 11:37:08 +02:00
match ( self , method ) {
( MethodFilter ::Any , _ )
| ( MethodFilter ::Connect , & Method ::CONNECT )
| ( MethodFilter ::Delete , & Method ::DELETE )
| ( MethodFilter ::Get , & Method ::GET )
| ( MethodFilter ::Head , & Method ::HEAD )
| ( MethodFilter ::Options , & Method ::OPTIONS )
| ( MethodFilter ::Patch , & Method ::PATCH )
| ( MethodFilter ::Post , & Method ::POST )
| ( MethodFilter ::Put , & Method ::PUT )
| ( MethodFilter ::Trace , & Method ::TRACE ) => true ,
_ => false ,
}
}
}
2021-06-04 01:00:48 +02:00
#[derive(Clone)]
pub struct Route < S , F > {
pub ( crate ) pattern : PathPattern ,
pub ( crate ) svc : S ,
pub ( crate ) fallback : F ,
2021-05-30 13:24:03 +02:00
}
2021-06-06 20:30:54 +02:00
pub trait RoutingDsl : Sized {
2021-06-04 01:00:48 +02:00
fn route < T > ( self , spec : & str , svc : T ) -> Route < T , Self >
where
2021-06-06 20:30:54 +02:00
T : Service < Request < Body > , Error = Infallible > + Clone ,
{
Route {
pattern : PathPattern ::new ( spec ),
svc ,
fallback : self ,
}
}
fn nest < T > ( self , spec : & str , svc : T ) -> Nested < T , Self >
where
T : Service < Request < Body > , Error = Infallible > + Clone ,
{
Nested {
pattern : PathPattern ::new ( spec ),
svc ,
fallback : self ,
}
}
2021-05-30 13:24:03 +02:00
2021-06-06 20:30:54 +02:00
fn boxed < B > ( self ) -> BoxRoute < B >
2021-05-30 13:24:03 +02:00
where
2021-06-04 01:00:48 +02:00
Self : Service < Request < Body > , Response = Response < B > , Error = Infallible > + Send + 'static ,
< Self as Service < Request < Body >>> ::Future : Send ,
2021-06-06 20:30:54 +02:00
B : http_body ::Body < Data = Bytes > + Send + Sync + 'static ,
B ::Error : Into < BoxError > + Send + Sync + 'static ,
2021-05-30 13:24:03 +02:00
{
2021-06-04 01:00:48 +02:00
ServiceBuilder ::new ()
. layer_fn ( BoxRoute )
. buffer ( 1024 )
. layer ( BoxService ::layer ())
. service ( self )
2021-05-30 13:24:03 +02:00
}
2021-06-06 20:30:54 +02:00
fn layer < L > ( self , layer : L ) -> Layered < L ::Service >
2021-06-04 01:00:48 +02:00
where
L : Layer < Self > ,
L ::Service : Service < Request < Body >> + Clone ,
{
Layered ( layer . layer ( self ))
2021-05-30 13:24:03 +02:00
}
}
2021-06-06 20:30:54 +02:00
impl < S , F > RoutingDsl for Route < S , F > {}
2021-05-30 13:24:03 +02:00
2021-06-04 01:00:48 +02:00
// ===== Routing service impls =====
2021-05-30 13:24:03 +02:00
2021-06-04 01:00:48 +02:00
impl < S , F , SB , FB > Service < Request < Body >> for Route < S , F >
2021-05-30 13:24:03 +02:00
where
2021-06-04 01:00:48 +02:00
S : Service < Request < Body > , Response = Response < SB > , Error = Infallible > + Clone ,
SB : http_body ::Body < Data = Bytes > + Send + Sync + 'static ,
SB ::Error : Into < BoxError > ,
F : Service < Request < Body > , Response = Response < FB > , Error = Infallible > + Clone ,
FB : http_body ::Body < Data = Bytes > + Send + Sync + 'static ,
FB ::Error : Into < BoxError > ,
2021-05-30 13:24:03 +02:00
{
2021-06-04 01:00:48 +02:00
type Response = Response < BoxBody > ;
type Error = Infallible ;
2021-05-30 13:24:03 +02:00
2021-06-04 01:00:48 +02:00
#[allow(clippy::type_complexity)]
type Future = future ::Either <
BoxResponseBody < Oneshot < S , Request < Body >>> ,
BoxResponseBody < Oneshot < F , Request < Body >>> ,
> ;
2021-05-30 13:24:03 +02:00
2021-06-04 01:00:48 +02:00
fn poll_ready ( & mut self , _cx : & mut Context < '_ > ) -> Poll < Result < (), Self ::Error >> {
Poll ::Ready ( Ok (()))
2021-05-30 15:44:26 +02:00
}
2021-06-04 01:00:48 +02:00
fn call ( & mut self , mut req : Request < Body > ) -> Self ::Future {
2021-06-06 20:30:54 +02:00
if let Some ( captures ) = self . pattern . full_match ( req . uri (). path ()) {
2021-06-04 01:00:48 +02:00
insert_url_params ( & mut req , captures );
let response_future = self . svc . clone (). oneshot ( req );
future ::Either ::Left ( BoxResponseBody ( response_future ))
} else {
let response_future = self . fallback . clone (). oneshot ( req );
future ::Either ::Right ( BoxResponseBody ( response_future ))
2021-06-02 22:07:37 +02:00
}
2021-06-04 01:00:48 +02:00
}
}
2021-05-30 15:44:26 +02:00
2021-06-04 01:00:48 +02:00
#[derive(Debug)]
pub ( crate ) struct UrlParams ( pub ( crate ) Vec < ( String , String ) > );
2021-06-02 22:07:37 +02:00
2021-06-04 01:00:48 +02:00
fn insert_url_params < B > ( req : & mut Request < B > , params : Vec < ( String , String ) > ) {
if let Some ( current ) = req . extensions_mut (). get_mut ::< Option < UrlParams >> () {
let mut current = current . take (). unwrap ();
current . 0. extend ( params );
req . extensions_mut (). insert ( Some ( current ));
} else {
req . extensions_mut (). insert ( Some ( UrlParams ( params )));
2021-05-30 13:24:03 +02:00
}
}
#[pin_project]
2021-06-06 15:19:54 +02:00
pub struct BoxResponseBody < F > ( #[pin] pub ( crate ) F );
2021-05-30 13:24:03 +02:00
2021-06-01 00:34:09 +02:00
impl < F , B > Future for BoxResponseBody < F >
2021-05-30 13:24:03 +02:00
where
2021-06-01 00:34:09 +02:00
F : Future < Output = Result < Response < B > , Infallible >> ,
2021-06-01 11:23:56 +02:00
B : http_body ::Body < Data = Bytes > + Send + Sync + 'static ,
2021-05-30 13:24:03 +02:00
B ::Error : Into < BoxError > ,
{
2021-06-01 11:23:56 +02:00
type Output = Result < Response < BoxBody > , Infallible > ;
2021-05-30 13:24:03 +02:00
fn poll ( self : Pin <& mut Self > , cx : & mut Context < '_ > ) -> Poll < Self ::Output > {
2021-06-01 00:34:09 +02:00
let response : Response < B > = ready! ( self . project (). 0. poll ( cx )). unwrap_infallible ();
2021-05-30 13:24:03 +02:00
let response = response . map ( | body | {
2021-06-01 00:34:09 +02:00
let body = body . map_err ( Into ::into );
2021-05-30 13:24:03 +02:00
BoxBody ::new ( body )
});
Poll ::Ready ( Ok ( response ))
}
}
2021-05-30 15:44:26 +02:00
2021-06-04 01:00:48 +02:00
#[derive(Clone, Copy)]
pub struct EmptyRouter ;
2021-06-06 20:30:54 +02:00
impl RoutingDsl for EmptyRouter {}
2021-05-31 16:28:26 +02:00
2021-06-04 01:00:48 +02:00
impl < R > Service < R > for EmptyRouter {
type Response = Response < Body > ;
type Error = Infallible ;
type Future = future ::Ready < Result < Self ::Response , Self ::Error >> ;
fn poll_ready ( & mut self , _cx : & mut Context < '_ > ) -> Poll < Result < (), Self ::Error >> {
Poll ::Ready ( Ok (()))
}
fn call ( & mut self , _req : R ) -> Self ::Future {
let mut res = Response ::new ( Body ::empty ());
* res . status_mut () = StatusCode ::NOT_FOUND ;
future ::ok ( res )
}
}
// ===== PathPattern =====
#[derive(Debug, Clone)]
pub ( crate ) struct PathPattern ( Arc < Inner > );
#[derive(Debug)]
struct Inner {
full_path_regex : Regex ,
capture_group_names : Box < [ Bytes ] > ,
}
impl PathPattern {
pub ( crate ) fn new ( pattern : & str ) -> Self {
let mut capture_group_names = Vec ::new ();
let pattern = pattern
. split ( '/' )
. map ( | part | {
if let Some ( key ) = part . strip_prefix ( ':' ) {
capture_group_names . push ( Bytes ::copy_from_slice ( key . as_bytes ()));
Cow ::Owned ( format! ( "(?P< {} >[^/]*)" , key ))
} else {
Cow ::Borrowed ( part )
}
})
. join ( "/" );
let full_path_regex =
2021-06-06 20:30:54 +02:00
Regex ::new ( & format! ( "^ {} " , pattern )). expect ( "invalid regex generated from route" );
2021-06-04 01:00:48 +02:00
Self ( Arc ::new ( Inner {
full_path_regex ,
capture_group_names : capture_group_names . into (),
}))
}
2021-06-06 20:30:54 +02:00
pub ( crate ) fn full_match ( & self , path : & str ) -> Option < Captures > {
self . do_match ( path ). and_then ( | match_ | {
if match_ . full_match {
Some ( match_ . captures )
} else {
None
}
})
}
pub ( crate ) fn prefix_match < 'a > ( & self , path : & 'a str ) -> Option < ( & 'a str , Captures ) > {
self . do_match ( path )
. map ( | match_ | ( match_ . matched , match_ . captures ))
}
fn do_match < 'a > ( & self , path : & 'a str ) -> Option < Match < 'a >> {
2021-06-04 01:00:48 +02:00
self . 0. full_path_regex . captures ( path ). map ( | captures | {
2021-06-06 20:30:54 +02:00
let matched = captures . get ( 0 ). unwrap ();
let full_match = matched . as_str () == path ;
2021-06-04 01:00:48 +02:00
let captures = self
. 0
. capture_group_names
. iter ()
. map ( | bytes | {
std ::str ::from_utf8 ( bytes )
. expect ( "bytes were created from str so is valid utf-8" )
})
. filter_map ( | name | captures . name ( name ). map ( | value | ( name , value . as_str ())))
. map ( | ( key , value ) | ( key . to_string (), value . to_string ()))
. collect ::< Vec < _ >> ();
2021-06-06 20:30:54 +02:00
Match {
captures ,
full_match ,
matched : matched . as_str (),
}
2021-06-04 01:00:48 +02:00
})
}
}
2021-06-06 20:30:54 +02:00
struct Match < 'a > {
captures : Captures ,
// true if regex matched whole path, false if it only matched a prefix
full_match : bool ,
matched : & 'a str ,
}
2021-06-04 01:00:48 +02:00
type Captures = Vec < ( String , String ) > ;
// ===== BoxRoute =====
pub struct BoxRoute < B > ( Buffer < BoxService < Request < Body > , Response < B > , Infallible > , Request < Body >> );
impl < B > Clone for BoxRoute < B > {
2021-05-31 16:28:26 +02:00
fn clone ( & self ) -> Self {
2021-06-04 01:00:48 +02:00
Self ( self . 0. clone ())
2021-05-31 16:28:26 +02:00
}
}
2021-06-06 20:30:54 +02:00
impl < B > RoutingDsl for BoxRoute < B > {}
2021-05-31 16:28:26 +02:00
2021-06-04 01:00:48 +02:00
impl < B > Service < Request < Body >> for BoxRoute < B >
2021-05-31 16:28:26 +02:00
where
2021-06-06 20:30:54 +02:00
B : http_body ::Body < Data = Bytes > + Send + Sync + 'static ,
B ::Error : Into < BoxError > + Send + Sync + 'static ,
2021-05-31 16:28:26 +02:00
{
2021-06-06 20:30:54 +02:00
type Response = Response < BoxBody > ;
2021-06-01 00:34:09 +02:00
type Error = Infallible ;
2021-06-04 01:00:48 +02:00
type Future = BoxRouteResponseFuture < B > ;
2021-05-31 16:28:26 +02:00
#[inline]
2021-06-04 01:00:48 +02:00
fn poll_ready ( & mut self , _cx : & mut Context < '_ > ) -> Poll < Result < (), Self ::Error >> {
Poll ::Ready ( Ok (()))
2021-05-31 16:28:26 +02:00
}
#[inline]
fn call ( & mut self , req : Request < Body > ) -> Self ::Future {
2021-06-04 01:00:48 +02:00
BoxRouteResponseFuture ( self . 0. clone (). oneshot ( req ))
2021-05-31 16:28:26 +02:00
}
}
#[pin_project]
2021-06-04 01:00:48 +02:00
pub struct BoxRouteResponseFuture < B > ( #[pin] InnerFuture < B > );
2021-05-31 16:28:26 +02:00
2021-06-04 01:00:48 +02:00
type InnerFuture < B > = Oneshot <
Buffer < BoxService < Request < Body > , Response < B > , Infallible > , Request < Body >> ,
Request < Body > ,
2021-05-31 16:28:26 +02:00
> ;
2021-06-04 01:00:48 +02:00
impl < B > Future for BoxRouteResponseFuture < B >
2021-06-01 00:34:09 +02:00
where
2021-06-06 20:30:54 +02:00
B : http_body ::Body < Data = Bytes > + Send + Sync + 'static ,
B ::Error : Into < BoxError > + Send + Sync + 'static ,
2021-06-01 00:34:09 +02:00
{
2021-06-06 20:30:54 +02:00
type Output = Result < Response < BoxBody > , Infallible > ;
2021-05-31 16:28:26 +02:00
fn poll ( self : Pin <& mut Self > , cx : & mut Context < '_ > ) -> Poll < Self ::Output > {
2021-06-04 01:00:48 +02:00
match ready! ( self . project (). 0. poll ( cx )) {
2021-06-06 20:30:54 +02:00
Ok ( res ) => Poll ::Ready ( Ok ( res . map ( BoxBody ::new ))),
2021-06-04 01:00:48 +02:00
Err ( err ) => Poll ::Ready ( Ok ( handle_buffer_error ( err ))),
2021-06-01 00:34:09 +02:00
}
2021-05-31 16:28:26 +02:00
}
}
2021-06-06 20:30:54 +02:00
fn handle_buffer_error ( error : BoxError ) -> Response < BoxBody > {
2021-06-01 00:34:09 +02:00
use tower ::buffer ::error ::{ Closed , ServiceError };
let error = match error . downcast ::< Closed > () {
Ok ( closed ) => {
return Response ::builder ()
. status ( StatusCode ::INTERNAL_SERVER_ERROR )
2021-06-06 20:30:54 +02:00
. body ( BoxBody ::new ( Full ::from ( closed . to_string ())))
2021-06-01 00:34:09 +02:00
. unwrap ();
}
Err ( e ) => e ,
};
let error = match error . downcast ::< ServiceError > () {
Ok ( service_error ) => {
return Response ::builder ()
. status ( StatusCode ::INTERNAL_SERVER_ERROR )
2021-06-06 20:30:54 +02:00
. body ( BoxBody ::new ( Full ::from ( format! ( "Service error: {} . This is a bug in tower-web. All inner services should be infallible. Please file an issue" , service_error ))))
2021-06-01 00:34:09 +02:00
. unwrap ();
}
Err ( e ) => e ,
};
Response ::builder ()
. status ( StatusCode ::INTERNAL_SERVER_ERROR )
2021-06-06 20:30:54 +02:00
. body ( BoxBody ::new ( Full ::from ( format! (
2021-06-01 00:34:09 +02:00
"Uncountered an unknown error: {} . This should never happen. Please file an issue" ,
error
2021-06-06 20:30:54 +02:00
))))
2021-06-01 00:34:09 +02:00
. unwrap ()
}
2021-06-04 01:00:48 +02:00
// ===== Layered =====
#[derive(Clone, Debug)]
pub struct Layered < S > ( S );
2021-06-06 20:30:54 +02:00
impl < S > RoutingDsl for Layered < S > {}
2021-06-04 01:00:48 +02:00
impl < S > Layered < S > {
2021-06-06 15:19:54 +02:00
pub fn handle_error < F , B , Res > ( self , f : F ) -> HandleError < S , F >
2021-06-04 01:00:48 +02:00
where
S : Service < Request < Body > , Response = Response < B >> + Clone ,
F : FnOnce ( S ::Error ) -> Res ,
Res : IntoResponse < Body > ,
B : http_body ::Body < Data = Bytes > + Send + Sync + 'static ,
B ::Error : Into < BoxError > + Send + Sync + 'static ,
{
2021-06-06 15:19:54 +02:00
HandleError { inner : self . 0 , f }
2021-06-04 01:00:48 +02:00
}
}
impl < S , B > Service < Request < Body >> for Layered < S >
where
2021-06-06 15:19:54 +02:00
S : Service < Request < Body > , Response = Response < B > , Error = Infallible > ,
2021-06-04 01:00:48 +02:00
{
type Response = S ::Response ;
2021-06-06 15:19:54 +02:00
type Error = Infallible ;
2021-06-04 01:00:48 +02:00
type Future = S ::Future ;
#[inline]
fn poll_ready ( & mut self , cx : & mut Context < '_ > ) -> Poll < Result < (), Self ::Error >> {
self . 0. poll_ready ( cx )
}
#[inline]
fn call ( & mut self , req : Request < Body > ) -> Self ::Future {
self . 0. call ( req )
}
}
#[derive(Clone, Copy)]
pub struct HandleError < S , F > {
inner : S ,
f : F ,
}
2021-06-06 20:30:54 +02:00
impl < S , F > RoutingDsl for HandleError < S , F > {}
2021-06-04 01:00:48 +02:00
impl < S , F , B , Res > Service < Request < Body >> for HandleError < S , F >
where
S : Service < Request < Body > , Response = Response < B >> + Clone ,
F : FnOnce ( S ::Error ) -> Res + Clone ,
Res : IntoResponse < Body > ,
B : http_body ::Body < Data = Bytes > + Send + Sync + 'static ,
B ::Error : Into < BoxError > + Send + Sync + 'static ,
{
type Response = Response < BoxBody > ;
type Error = Infallible ;
type Future = HandleErrorFuture < Oneshot < S , Request < Body >> , F > ;
fn poll_ready ( & mut self , _cx : & mut Context < '_ > ) -> Poll < Result < (), Self ::Error >> {
Poll ::Ready ( Ok (()))
}
fn call ( & mut self , req : Request < Body > ) -> Self ::Future {
HandleErrorFuture {
inner : self . inner . clone (). oneshot ( req ),
f : Some ( self . f . clone ()),
}
}
}
#[pin_project]
pub struct HandleErrorFuture < Fut , F > {
#[pin]
inner : Fut ,
f : Option < F > ,
}
impl < Fut , F , B , E , Res > Future for HandleErrorFuture < Fut , F >
where
Fut : Future < Output = Result < Response < B > , E >> ,
F : FnOnce ( E ) -> Res ,
Res : IntoResponse < Body > ,
B : http_body ::Body < Data = Bytes > + Send + Sync + 'static ,
B ::Error : Into < BoxError > + Send + Sync + 'static ,
{
type Output = Result < Response < BoxBody > , Infallible > ;
fn poll ( self : Pin <& mut Self > , cx : & mut Context < '_ > ) -> Poll < Self ::Output > {
let this = self . project ();
match ready! ( this . inner . poll ( cx )) {
Ok ( res ) => Ok ( res . map ( BoxBody ::new )). into (),
Err ( err ) => {
let f = this . f . take (). unwrap ();
let res = f ( err ). into_response ();
Ok ( res . map ( BoxBody ::new )). into ()
}
}
2021-06-02 22:07:37 +02:00
}
}
2021-06-06 20:30:54 +02:00
// ===== nesting =====
pub fn nest < S > ( spec : & str , svc : S ) -> Nested < S , EmptyRouter >
where
S : Service < Request < Body > , Error = Infallible > + Clone ,
{
Nested {
pattern : PathPattern ::new ( spec ),
svc ,
fallback : EmptyRouter ,
}
}
#[derive(Debug, Clone)]
pub struct Nested < S , F > {
pattern : PathPattern ,
svc : S ,
fallback : F ,
}
impl < S , F > RoutingDsl for Nested < S , F > {}
impl < S , F , SB , FB > Service < Request < Body >> for Nested < S , F >
where
S : Service < Request < Body > , Response = Response < SB > , Error = Infallible > + Clone ,
SB : http_body ::Body < Data = Bytes > + Send + Sync + 'static ,
SB ::Error : Into < BoxError > ,
F : Service < Request < Body > , Response = Response < FB > , Error = Infallible > + Clone ,
FB : http_body ::Body < Data = Bytes > + Send + Sync + 'static ,
FB ::Error : Into < BoxError > ,
{
type Response = Response < BoxBody > ;
type Error = Infallible ;
#[allow(clippy::type_complexity)]
type Future = future ::Either <
BoxResponseBody < Oneshot < S , Request < Body >>> ,
BoxResponseBody < Oneshot < F , Request < Body >>> ,
> ;
fn poll_ready ( & mut self , _cx : & mut Context < '_ > ) -> Poll < Result < (), Self ::Error >> {
Poll ::Ready ( Ok (()))
}
fn call ( & mut self , mut req : Request < Body > ) -> Self ::Future {
if let Some (( prefix , captures )) = self . pattern . prefix_match ( req . uri (). path ()) {
let without_prefix = strip_prefix ( req . uri (), prefix );
* req . uri_mut () = without_prefix ;
insert_url_params ( & mut req , captures );
let response_future = self . svc . clone (). oneshot ( req );
future ::Either ::Left ( BoxResponseBody ( response_future ))
} else {
let response_future = self . fallback . clone (). oneshot ( req );
future ::Either ::Right ( BoxResponseBody ( response_future ))
}
}
}
fn strip_prefix ( uri : & Uri , prefix : & str ) -> Uri {
let path_and_query = if let Some ( path_and_query ) = uri . path_and_query () {
let new_path = if let Some ( path ) = path_and_query . path (). strip_prefix ( prefix ) {
path
} else {
path_and_query . path ()
};
if let Some ( query ) = path_and_query . query () {
Some (
format! ( " {} ? {} " , new_path , query )
. parse ::< http ::uri ::PathAndQuery > ()
. unwrap (),
)
} else {
Some ( new_path . parse (). unwrap ())
}
} else {
None
};
let mut parts = http ::uri ::Parts ::default ();
parts . scheme = uri . scheme (). cloned ();
parts . authority = uri . authority (). cloned ();
parts . path_and_query = path_and_query ;
Uri ::from_parts ( parts ). unwrap ()
}
2021-05-30 15:44:26 +02:00
#[cfg(test)]
mod tests {
use super ::* ;
#[test]
fn test_routing () {
2021-06-04 01:00:48 +02:00
assert_match ( "/" , "/" );
assert_match ( "/foo" , "/foo" );
assert_match ( "/foo/" , "/foo/" );
refute_match ( "/foo" , "/foo/" );
refute_match ( "/foo/" , "/foo" );
assert_match ( "/foo/bar" , "/foo/bar" );
refute_match ( "/foo/bar/" , "/foo/bar" );
refute_match ( "/foo/bar" , "/foo/bar/" );
assert_match ( "/:value" , "/foo" );
assert_match ( "/users/:id" , "/users/1" );
assert_match ( "/users/:id/action" , "/users/42/action" );
refute_match ( "/users/:id/action" , "/users/42" );
refute_match ( "/users/:id" , "/users/42/action" );
2021-05-30 15:44:26 +02:00
}
2021-06-04 01:00:48 +02:00
fn assert_match ( route_spec : & 'static str , path : & 'static str ) {
let route = PathPattern ::new ( route_spec );
2021-05-30 15:44:26 +02:00
assert! (
2021-06-06 20:30:54 +02:00
route . full_match ( path ). is_some (),
2021-06-04 01:00:48 +02:00
"`{}` doesn't match `{}`" ,
path ,
route_spec
2021-05-30 15:44:26 +02:00
);
}
2021-06-04 01:00:48 +02:00
fn refute_match ( route_spec : & 'static str , path : & 'static str ) {
let route = PathPattern ::new ( route_spec );
2021-05-30 15:44:26 +02:00
assert! (
2021-06-06 20:30:54 +02:00
route . full_match ( path ). is_none (),
2021-06-04 01:00:48 +02:00
"`{}` did match `{}` (but shouldn't)" ,
path ,
route_spec
2021-06-02 22:07:37 +02:00
);
}
2021-05-30 15:44:26 +02:00
}