diff --git a/src/handler.rs b/src/handler.rs index b83e37a..12a0151 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -5,17 +5,10 @@ use crate::HTTPResult; use async_trait::async_trait; use http::{Request, Response}; -#[derive(Debug, Clone)] -pub struct Params(BTreeMap); - -impl Default for Params { - fn default() -> Self { - Self(BTreeMap::default()) - } -} +pub(crate) type Params = BTreeMap<&'static str, &'static str>; pub type HandlerFunc = - dyn Fn(Request, Params, Option>) -> HTTPResult + Sync; + dyn Fn(Request, Option>, Params) -> HTTPResult + Sync; #[async_trait] pub trait Handler @@ -26,6 +19,7 @@ where &self, req: Request, response: Option>, + params: Params, ) -> HTTPResult; } @@ -34,7 +28,6 @@ pub struct BasicHandler where Self: Sync + Sized, { - params: Params, next: Option>, func: &'static HandlerFunc, } @@ -43,12 +36,8 @@ impl BasicHandler where Self: Sync + Sized, { - pub fn new( - params: Params, - next: Option>, - func: &'static HandlerFunc, - ) -> Self { - Self { params, next, func } + pub fn new(next: Option>, func: &'static HandlerFunc) -> Self { + Self { next, func } } } @@ -61,10 +50,16 @@ where &self, req: Request, response: Option>, + params: Params, ) -> HTTPResult { - let (req, response) = (*self.func)(req, self.params.clone(), response)?; + let (req, response) = (*self.func)(req, response, params.clone())?; if self.next.is_some() { - return Ok(self.next.clone().unwrap().perform(req, response).await?); + return Ok(self + .next + .clone() + .unwrap() + .perform(req, response, params) + .await?); } Ok((req, response)) @@ -84,8 +79,8 @@ mod tests { #[allow(dead_code)] fn one( mut req: Request, - _params: Params, _response: Option>, + _params: Params, ) -> HTTPResult { let headers = req.headers_mut(); headers.insert("wakka", HeaderValue::from_str("wakka wakka").unwrap()); @@ -96,8 +91,8 @@ mod tests { #[allow(dead_code)] fn two( req: Request, - _params: Params, mut response: Option>, + _params: Params, ) -> HTTPResult { if let Some(header) = req.headers().get("wakka") { if header != "wakka wakka" { @@ -126,9 +121,9 @@ mod tests { use std::sync::Arc; // single stage handler that never yields a response - let bh = super::BasicHandler::new(Params::default(), None, &one); + let bh = super::BasicHandler::new(None, &one); let req = Request::default(); - let (req, response) = bh.perform(req, None).await.unwrap(); + let (req, response) = bh.perform(req, None, Params::new()).await.unwrap(); if !req.headers().get("wakka").is_some() { panic!("no wakkas") } @@ -138,15 +133,19 @@ mod tests { } // two-stage handler; yields a response if the first one was good. - let bh_two = super::BasicHandler::new(Params::default(), None, &two); - let bh = super::BasicHandler::new(Params::default(), Some(Arc::new(bh_two.clone())), &one); - let (_, response) = bh.perform(req, None).await.unwrap(); + let bh_two = super::BasicHandler::new(None, &two); + let bh = super::BasicHandler::new(Some(Arc::new(bh_two.clone())), &one); + let (_, response) = bh.perform(req, None, Params::new()).await.unwrap(); if !(response.is_some() && response.unwrap().status() == StatusCode::OK) { panic!("response not ok") } - if !bh_two.perform(Request::default(), None).await.is_err() { + if !bh_two + .perform(Request::default(), None, Params::new()) + .await + .is_err() + { panic!("no error") } } diff --git a/src/lib.rs b/src/lib.rs index 55c84b7..0aaefc6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ pub mod handler; pub mod path; -//pub mod router; +pub mod router; use http::{Request, Response}; diff --git a/src/path.rs b/src/path.rs index 1ff794d..f8d37a6 100644 --- a/src/path.rs +++ b/src/path.rs @@ -1,17 +1,29 @@ -use std::collections::BTreeMap; +use crate::{handler::Params, Error}; -use crate::Error; - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialOrd, PartialEq)] pub enum RoutePart { PathComponent(&'static str), Param(&'static str), Leader, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialOrd)] pub struct Path(Vec); +impl PartialEq for Path { + fn eq(&self, other: &Self) -> bool { + self.to_string() == other.to_string() + } +} + +impl Eq for Path {} + +impl Ord for Path { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.to_string().cmp(&other.to_string()) + } +} + impl Path { pub(crate) fn new(path: &'static str) -> Self { let mut parts = Self::default(); @@ -47,12 +59,9 @@ impl Path { params } - pub(crate) fn extract( - &self, - provided: &'static str, - ) -> Result, Error> { + pub(crate) fn extract(&self, provided: &'static str) -> Result { let parts: Vec<&str> = provided.split("/").collect(); - let mut params = BTreeMap::new(); + let mut params = Params::default(); if parts.len() != self.0.len() { return Err(Error::new("invalid parameters")); diff --git a/src/router.rs b/src/router.rs new file mode 100644 index 0000000..59ae8c8 --- /dev/null +++ b/src/router.rs @@ -0,0 +1,82 @@ +use std::collections::{BTreeMap, BTreeSet, HashSet}; + +use http::Request; + +use crate::{ + handler::{BasicHandler, Handler}, + path::Path, + Error, HTTPResult, +}; + +#[derive(Clone)] +pub struct Route { + method: http::Method, + path: Path, + handler: BasicHandler, +} + +impl PartialEq for Route { + fn eq(&self, other: &Self) -> bool { + let left = self.method.to_string() + " " + &self.path.to_string(); + let right = other.method.to_string() + " " + &other.path.to_string(); + left == right + } +} + +impl Eq for Route {} + +impl PartialOrd for Route { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Route { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + let left = self.method.to_string() + " " + &self.path.to_string(); + let right = other.method.to_string() + " " + &other.path.to_string(); + + left.to_string().cmp(&right.to_string()) + } +} + +impl Route { + fn new(method: http::Method, path: &'static str, handler: BasicHandler) -> Self { + Self { + method, + handler, + path: Path::new(path), + } + } + + async fn dispatch(&self, provided: &'static str, req: Request) -> HTTPResult { + let params = self.path.extract(provided)?; + self.handler.perform(req, None, params).await + } +} + +#[derive(Clone)] +pub struct Router(BTreeSet); + +impl Router { + pub fn new() -> Self { + Self(BTreeSet::new()) + } + + pub fn add(&mut self, method: http::Method, path: &'static str, bh: BasicHandler) -> Self { + self.0.insert(Route::new(method, path, bh)); + self.clone() + } + + pub fn find(&self, req: &'static Request) -> Result { + let path = req.uri().path(); + + for route_path in self.0.clone() { + if route_path.path.matches(path) && route_path.method.eq(req.method()) { + return Ok(route_path.handler); + } + } + + Err(Error::new("no route found for request")) + } +}