@ -5,7 +5,10 @@ use hyper::{
} ;
use std ::{
fmt ,
marker ::Send ,
marker ::{
Send ,
Sync ,
} ,
iter ,
} ;
use tokio ::{
@ -35,6 +38,16 @@ pub trait UriRoute
{
""
}
#[ inline ] fn type_name ( & self ) -> & str
{
std ::any ::type_name ::< Self > ( )
}
#[ inline ] fn mutate_uri ( & self , uri : String ) -> String
{
uri
}
}
impl UriRoute for str
@ -67,19 +80,68 @@ impl UriRoute for regex::Regex
}
}
/// A router for all under a prefix
#[ derive(Debug, Clone, PartialEq, Hash) ]
pub struct PrefixRouter ( String ) ;
impl PrefixRouter
{
/// Create a new instance with this string
pub fn new ( string : impl Into < String > ) -> Self
{
Self ( string . into ( ) )
}
}
impl UriRoute for PrefixRouter
{
#[ inline ] fn is_match ( & self , uri : & str ) -> bool {
uri . starts_with ( self . 0. as_str ( ) )
}
#[ inline ] fn as_string ( & self ) -> & str {
self . 0. as_str ( )
}
fn mutate_uri ( & self , mut uri : String ) -> String {
uri . replace_range ( .. self . 0. len ( ) , "" ) ;
uri
}
}
impl fmt ::Display for PrefixRouter
{
fn fmt ( & self , f : & mut fmt ::Formatter < ' _ > ) -> fmt ::Result
{
write! ( f , "{}*" , self . 0 )
}
}
/// Contains a routing table
#[ derive(Debug) ]
pub struct Router
pub struct Router < T : Send >
{
routes : Arena < ( Option < Method > , OpaqueDebug < Box < dyn UriRoute + Send + ' static > > , mpsc ::Sender < String > ) > ,
routes : Arena < ( Option < Method > , OpaqueDebug < Box < dyn UriRoute + Send + Sync + ' static > > , mpsc ::Sender < ( String , T ) > ) > ,
}
impl Router
impl < T : Send > fmt ::Display for Router < T >
{
fn fmt ( & self , f : & mut fmt ::Formatter < ' _ > ) -> fmt ::Result
{
write! ( f , "Router {{ routes: " ) ? ;
for ( i , ( method , route , _ ) ) in self . routes . iter ( ) {
writeln! ( f , "\t ({:?} => ({:?}, {} ({:?})))," , i , method , route . type_name ( ) , route . as_string ( ) ) ? ;
}
write! ( f , "}}" )
}
}
impl < T : Send + Clone > Router < T >
{
/// Create an empty routing table
pub fn new ( ) -> Self
{
Self {
Self {
routes : Arena ::new ( ) ,
}
}
@ -88,17 +150,27 @@ impl Router
///
/// # Returns
/// The hook's new index, and the receiver that `dispatch()` sends to.
pub fn hook < Uri : UriRoute + Send + ' static > ( & mut self , method : Option < Method > , uri : Uri ) -> ( Index , mpsc ::Receiver < String > )
pub fn hook < Uri : UriRoute + Send + Sync + ' static > ( & mut self , method : Option < Method > , uri : Uri ) -> ( Index , mpsc ::Receiver < ( String , T ) > )
{
let ( tx , rx ) = mpsc ::channel ( config ::get_or_default ( ) . dos_max ) ;
( self . routes . insert ( ( method , OpaqueDebug ::new ( Box ::new ( uri ) ) , tx ) ) , rx )
}
/// Remove all hooks
pub fn clear ( & mut self )
{
self . routes . clear ( ) ;
}
/// Dispatch the URI location across this router, sending to all that match it.
///
/// # Timeout
/// The timeout is waited on the *individual* dispatches. If you want a global timeout, please timeout the future returned by this function instead.
/// Timed-out dispatches are counted the same as sending errors.
///
/// # Returns
/// When one or more dispatchers match but faile, `Err` is returned. Inside the `Err` tuple is the amount of successful dispatches, and also a vector containing the indecies of the failed hook sends.
pub async fn dispatch ( & mut self , method : & Method , uri : impl AsRef < str > , timeout : Option < time ::Duration > ) -> Result < usize , ( usize , Vec < Index > ) >
pub async fn dispatch ( & mut self , method : & Method , uri : impl AsRef < str > , nonce: T , timeout: Option < time ::Duration > ) -> Result < usize , ( usize , Vec < Index > ) >
{
let string = uri . as_ref ( ) ;
let mut success = 0 usize ;
@ -109,25 +181,29 @@ impl Router
Some ( x ) if x ! = method = > None ,
_ = > {
if route . is_match ( string ) {
trace ! ( "{:?} @{} : -> {}", i , route . as_string ( ) , string ) ;
trace ! ( "{:?} `{}` : -> {}", i , route . as_string ( ) , string ) ;
let timeout = timeout . clone ( ) ;
let nonce = nonce . clone ( ) ;
macro_rules! send {
( ) = > {
match timeout {
None = > sender . send ( string . to_owned ( ) ) . await
. map_err ( | e | SendTimeoutError ::Closed ( e . 0 ) ) ,
Some ( time ) = > sender . send_timeout ( string . to_owned ( ) , time ) . await
{
let string = route . mutate_uri ( string . to_owned ( ) ) ;
match timeout {
None = > sender . send ( ( string , nonce ) ) . await
. map_err ( | e | SendTimeoutError ::Closed ( e . 0 ) ) ,
Some ( time ) = > sender . send_timeout ( ( string , nonce ) , time ) . await
}
}
}
} ;
Some ( async move {
match send ! ( ) {
Err ( SendTimeoutError ::Closed ( er ) ) = > {
error ! ( "{:?}: Dispatch failed on hooked route for {}", i , er ) ;
error ! ( "{:?}: Dispatch failed on hooked route for ` {}` ", i , er . 0 ) ;
Err ( i )
} ,
Err ( SendTimeoutError ::Timeout ( er ) ) = > {
warn ! ( "{:?}: Dispatch timed out on hooked route for {}", i , er ) ;
warn ! ( "{:?}: Dispatch timed out on hooked route for ` {}` ", i , er . 0 ) ;
Err ( i )
} ,
_ = > Ok ( ( ) ) ,
@ -152,6 +228,21 @@ impl Router
}
}
/// Forcefully dispatch `uri` on hook `which`, regardless of method or URI matching.
///
/// # Returns
/// If `which` is not contained within the table, immediately returns `None`, otherwise returns a future that completes when the dispatch is complete.
/// Note: This future must be `await`ed for the dispatch to happen.
pub fn dispatch_force ( & mut self , which : Index , uri : String , nonce : T , timeout : Option < time ::Duration > ) -> Option < impl Future < Output = Result < ( ) , SendTimeoutError < ( String , T ) > > > + ' _ >
{
self . routes . get_mut ( which ) . map ( move | ( _ , _ , send ) | {
match timeout {
Some ( timeout ) = > send . send_timeout ( ( uri , nonce ) , timeout ) . boxed ( ) ,
None = > send . send ( ( uri , nonce ) ) . map ( | res | res . map_err ( | e | SendTimeoutError ::Closed ( e . 0 ) ) ) . boxed ( ) ,
}
} )
}
/// Attempt to unhook these hooks. If one or more of the provided indecies does not exist in the routing table, it is ignored.
pub fn unhook < I > ( & mut self , items : I )
where I : IntoIterator < Item = Index >