mirror of
https://github.com/zerotier/ratpack.git
synced 2026-05-22 16:27:23 -07:00
Router implementation and a few trait impls to make things happy
Signed-off-by: Erik Hollensbe <linux@hollensbe.org>
This commit is contained in:
+25
-26
@@ -5,17 +5,10 @@ use crate::HTTPResult;
|
||||
use async_trait::async_trait;
|
||||
use http::{Request, Response};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Params(BTreeMap<String, String>);
|
||||
|
||||
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<hyper::Body>, Params, Option<Response<hyper::Body>>) -> HTTPResult + Sync;
|
||||
dyn Fn(Request<hyper::Body>, Option<Response<hyper::Body>>, Params) -> HTTPResult + Sync;
|
||||
|
||||
#[async_trait]
|
||||
pub trait Handler
|
||||
@@ -26,6 +19,7 @@ where
|
||||
&self,
|
||||
req: Request<hyper::Body>,
|
||||
response: Option<Response<hyper::Body>>,
|
||||
params: Params,
|
||||
) -> HTTPResult;
|
||||
}
|
||||
|
||||
@@ -34,7 +28,6 @@ pub struct BasicHandler
|
||||
where
|
||||
Self: Sync + Sized,
|
||||
{
|
||||
params: Params,
|
||||
next: Option<Arc<BasicHandler>>,
|
||||
func: &'static HandlerFunc,
|
||||
}
|
||||
@@ -43,12 +36,8 @@ impl BasicHandler
|
||||
where
|
||||
Self: Sync + Sized,
|
||||
{
|
||||
pub fn new(
|
||||
params: Params,
|
||||
next: Option<Arc<BasicHandler>>,
|
||||
func: &'static HandlerFunc,
|
||||
) -> Self {
|
||||
Self { params, next, func }
|
||||
pub fn new(next: Option<Arc<BasicHandler>>, func: &'static HandlerFunc) -> Self {
|
||||
Self { next, func }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,10 +50,16 @@ where
|
||||
&self,
|
||||
req: Request<hyper::Body>,
|
||||
response: Option<Response<hyper::Body>>,
|
||||
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<Body>,
|
||||
_params: Params,
|
||||
_response: Option<Response<Body>>,
|
||||
_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<Body>,
|
||||
_params: Params,
|
||||
mut response: Option<Response<Body>>,
|
||||
_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")
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
pub mod handler;
|
||||
pub mod path;
|
||||
//pub mod router;
|
||||
pub mod router;
|
||||
|
||||
use http::{Request, Response};
|
||||
|
||||
|
||||
+19
-10
@@ -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<RoutePart>);
|
||||
|
||||
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<BTreeMap<&'static str, &str>, Error> {
|
||||
pub(crate) fn extract(&self, provided: &'static str) -> Result<Params, Error> {
|
||||
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"));
|
||||
|
||||
@@ -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<std::cmp::Ordering> {
|
||||
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<hyper::Body>) -> HTTPResult {
|
||||
let params = self.path.extract(provided)?;
|
||||
self.handler.perform(req, None, params).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Router(BTreeSet<Route>);
|
||||
|
||||
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<hyper::Body>) -> Result<BasicHandler, Error> {
|
||||
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"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user