uhttp 0.17.0

Simple http server for humans
Documentation
use std::cell::RefCell;
use std::collections::HashMap;
use std::pin::Pin;
use std::rc::Rc;
use std::sync::Arc;

use http::Method;
use path_tree::PathTree;
use percent_encoding::percent_decode_str;
use tokio::io::AsyncWriteExt;

use super::RouteBuilder;
use super::RouteBuilderNc;
use crate::Request;
use crate::Response;

pub(super) type RouterHandleFuncInner<Context> = Arc<
  dyn 'static
    + Send
    + Sync
    + Fn(
      Request,
      Response,
      Context,
    ) -> Pin<Box<dyn 'static + Send + Future<Output = crate::Result<()>>>>,
>;

pub(super) type RouterMiddlewareFuncInner<Context> = Arc<
  dyn 'static
    + Send
    + Sync
    + Fn(
      Request,
      Response,
      Context,
    ) -> Pin<
      Box<
        dyn 'static + Send + Future<Output = crate::Result<Option<(Request, Response, Context)>>>,
      >,
    >,
>;

pub type RouterHandleFunc<Context> = Box<
  dyn 'static
    + Send
    + Sync
    + Fn(
      Request,
      Response,
      Context,
    ) -> Pin<Box<dyn 'static + Send + Future<Output = crate::Result<()>>>>,
>;

pub type RouterMiddlewareFunc<Context> = Box<
  dyn 'static
    + Send
    + Sync
    + Fn(
      Request,
      Response,
      Context,
    ) -> Pin<
      Box<
        dyn 'static + Send + Future<Output = crate::Result<Option<(Request, Response, Context)>>>,
      >,
    >,
>;

pub struct Router<T>
where
  T: Clone + Send + Sync + 'static,
{
  middleware: Vec<RouterMiddlewareFuncInner<T>>,
  any_routes: Rc<RefCell<PathTree<(Vec<RouterMiddlewareFuncInner<T>>, RouterHandleFuncInner<T>)>>>,
  get_routes: Rc<RefCell<PathTree<(Vec<RouterMiddlewareFuncInner<T>>, RouterHandleFuncInner<T>)>>>,
  post_routes: Rc<RefCell<PathTree<(Vec<RouterMiddlewareFuncInner<T>>, RouterHandleFuncInner<T>)>>>,
  put_routes: Rc<RefCell<PathTree<(Vec<RouterMiddlewareFuncInner<T>>, RouterHandleFuncInner<T>)>>>,
  patch_routes:
    Rc<RefCell<PathTree<(Vec<RouterMiddlewareFuncInner<T>>, RouterHandleFuncInner<T>)>>>,
  delete_routes:
    Rc<RefCell<PathTree<(Vec<RouterMiddlewareFuncInner<T>>, RouterHandleFuncInner<T>)>>>,
  context: T,
}

impl Router<()> {
  pub fn new_without_context() -> Router<()> {
    Self::new(())
  }
}

impl<T: Clone + Send + Sync + 'static> Router<T> {
  pub fn new(context: T) -> Self {
    Self {
      middleware: Vec::new(),
      any_routes: Rc::new(RefCell::new(PathTree::new())),
      get_routes: Rc::new(RefCell::new(PathTree::new())),
      post_routes: Rc::new(RefCell::new(PathTree::new())),
      put_routes: Rc::new(RefCell::new(PathTree::new())),
      patch_routes: Rc::new(RefCell::new(PathTree::new())),
      delete_routes: Rc::new(RefCell::new(PathTree::new())),
      context,
    }
  }

  pub fn with_all<F, Fut>(
    &mut self,
    middleware: F,
  ) where
    F: 'static + Send + Sync + Fn(Request, Response, T) -> Fut,
    Fut: 'static + Send + Future<Output = crate::Result<Option<(Request, Response, T)>>>,
  {
    self.middleware.push(Arc::new(move |req, res, ctx| {
      Box::pin(middleware(req, res, ctx))
    }));
  }

  pub fn with<F, Fut>(
    &mut self,
    middleware: F,
  ) -> RouteBuilder<T>
  where
    F: 'static + Send + Sync + Fn(Request, Response, T) -> Fut,
    Fut: 'static + Send + Future<Output = crate::Result<Option<(Request, Response, T)>>>,
  {
    let middleware: RouterMiddlewareFuncInner<T> =
      Arc::new(move |req, res, ctx| Box::pin(middleware(req, res, ctx)));

    RouteBuilder {
      middleware: vec![middleware],
      any_routes: Rc::clone(&self.any_routes),
      get_routes: Rc::clone(&self.get_routes),
      post_routes: Rc::clone(&self.post_routes),
      put_routes: Rc::clone(&self.put_routes),
      patch_routes: Rc::clone(&self.patch_routes),
      delete_routes: Rc::clone(&self.delete_routes),
    }
  }

  pub fn without_context(&mut self) -> RouteBuilderNc<T> {
    RouteBuilderNc {
      any_routes: Rc::clone(&self.any_routes),
      get_routes: Rc::clone(&self.get_routes),
      post_routes: Rc::clone(&self.post_routes),
      put_routes: Rc::clone(&self.put_routes),
      patch_routes: Rc::clone(&self.patch_routes),
      delete_routes: Rc::clone(&self.delete_routes),
    }
  }

  pub fn get<F, Fut>(
    &mut self,
    route: &str,
    handler: F,
  ) where
    F: 'static + Send + Sync + Fn(Request, Response, T) -> Fut,
    Fut: 'static + Send + Future<Output = crate::Result<()>>,
  {
    let _ = self.get_routes.borrow_mut().insert(
      route,
      (
        Vec::new(),
        Arc::new(move |req, res, ctx| Box::pin(handler(req, res, ctx))),
      ),
    );
  }

  pub fn post<F, Fut>(
    &mut self,
    route: &str,
    handler: F,
  ) where
    F: 'static + Send + Sync + Fn(Request, Response, T) -> Fut,
    Fut: 'static + Send + Future<Output = crate::Result<()>>,
  {
    let _ = self.post_routes.borrow_mut().insert(
      route,
      (
        Vec::new(),
        Arc::new(move |req, res, ctx| Box::pin(handler(req, res, ctx))),
      ),
    );
  }

  pub fn put<F, Fut>(
    &mut self,
    route: &str,
    handler: F,
  ) where
    F: 'static + Send + Sync + Fn(Request, Response, T) -> Fut,
    Fut: 'static + Send + Future<Output = crate::Result<()>>,
  {
    let _ = self.put_routes.borrow_mut().insert(
      route,
      (
        Vec::new(),
        Arc::new(move |req, res, ctx| Box::pin(handler(req, res, ctx))),
      ),
    );
  }

  pub fn patch<F, Fut>(
    &mut self,
    route: &str,
    handler: F,
  ) where
    F: 'static + Send + Sync + Fn(Request, Response, T) -> Fut,
    Fut: 'static + Send + Future<Output = crate::Result<()>>,
  {
    let _ = self.patch_routes.borrow_mut().insert(
      route,
      (
        Vec::new(),
        Arc::new(move |req, res, ctx| Box::pin(handler(req, res, ctx))),
      ),
    );
  }

  pub fn delete<F, Fut>(
    &mut self,
    route: &str,
    handler: F,
  ) where
    F: 'static + Send + Sync + Fn(Request, Response, T) -> Fut,
    Fut: 'static + Send + Future<Output = crate::Result<()>>,
  {
    let _ = self.delete_routes.borrow_mut().insert(
      route,
      (
        Vec::new(),
        Arc::new(move |req, res, ctx| Box::pin(handler(req, res, ctx))),
      ),
    );
  }

  pub fn any<F, Fut>(
    &mut self,
    route: &str,
    handler: F,
  ) where
    F: 'static + Send + Sync + Fn(Request, Response, T) -> Fut,
    Fut: 'static + Send + Future<Output = crate::Result<()>>,
  {
    let _ = self.any_routes.borrow_mut().insert(
      route,
      (
        Vec::new(),
        Arc::new(move |req, res, ctx| Box::pin(handler(req, res, ctx))),
      ),
    );
  }

  pub fn handler(&self) -> crate::HandleFunc {
    let middleware = Arc::new(self.middleware.clone());
    let any_routes = Arc::new(self.any_routes.borrow().clone());
    let get_routes = Arc::new(self.get_routes.borrow().clone());
    let post_routes = Arc::new(self.post_routes.borrow().clone());
    let put_routes = Arc::new(self.put_routes.borrow().clone());
    let patch_routes = Arc::new(self.patch_routes.borrow().clone());
    let delete_routes = Arc::new(self.delete_routes.borrow().clone());
    let context = self.context.clone();

    Box::new(move |mut req, mut res| {
      let middleware = middleware.clone();
      let any_routes = any_routes.clone();
      let get_routes = get_routes.clone();
      let post_routes = post_routes.clone();
      let put_routes = put_routes.clone();
      let patch_routes = patch_routes.clone();
      let delete_routes = delete_routes.clone();
      let mut context = context.clone();

      Box::pin(async move {
        for middleware in &*middleware {
          let Some((new_req, new_res, new_context)) = middleware(req, res, context).await? else {
            return Ok(());
          };
          req = new_req;
          res = new_res;
          context = new_context;
        }

        let path = req.uri.path().to_string();
        let route = any_routes.find(&path);

        if route.is_none() {
          let routes = match req.method() {
            &Method::GET => get_routes,
            &Method::POST => post_routes,
            &Method::PUT => put_routes,
            &Method::PATCH => patch_routes,
            &Method::DELETE => delete_routes,
            _ => Arc::clone(&any_routes),
          };

          let route = routes.find(&path);
          if let Some(((middleware, handler), params)) = route {
            for middleware in middleware {
              let Some((new_req, new_res, new_context)) = middleware(req, res, context).await?
              else {
                return Ok(());
              };
              req = new_req;
              res = new_res;
              context = new_context;
            }

            let params_map: HashMap<String, String> = params
              .params()
              .iter()
              .map(|(k, v)| {
                (
                  k.to_string(),
                  percent_decode_str(v).decode_utf8_lossy().to_string(),
                )
              })
              .collect();
            req.params = params_map;
            handler(req, res, context).await?;
            return Ok(());
          }
        }

        if let Some(((middleware, handler), params)) = route {
          for middleware in middleware {
            let Some((new_req, new_res, new_context)) = middleware(req, res, context).await? else {
              return Ok(());
            };
            req = new_req;
            res = new_res;
            context = new_context;
          }

          let params_map: HashMap<String, String> = params
            .params()
            .iter()
            .map(|(k, v)| {
              (
                k.to_string(),
                percent_decode_str(v).decode_utf8_lossy().to_string(),
              )
            })
            .collect();
          req.params = params_map;
          handler(req, res, context).await?;
          return Ok(());
        }

        res.write_all(b"").await?;
        res.write_head(crate::StatusCode::NOT_FOUND).await?;
        Ok(())
      })
    })
  }
}