fuel-web-utils 0.0.29

Fuel library for web utils
use std::convert::Infallible;

use axum::{
    extract::Request,
    response::IntoResponse,
    routing::{MethodRouter, Route},
    Router,
};
use tower::{Layer, Service};

pub struct RouterBuilder<S: Send + Sync + Clone + 'static> {
    router: Router<S>,
    base_path: String,
    prefix: Option<String>,
}

impl<S: Send + Sync + Clone + 'static> RouterBuilder<S> {
    pub fn new(base_path: &str) -> Self {
        Self {
            router: Router::new(),
            base_path: Self::safe_path(base_path),
            prefix: None,
        }
    }

    pub fn path(&self) -> String {
        self.base_path.to_string()
    }

    pub fn root(mut self, handler: MethodRouter<S>) -> Self {
        self.router = self.router.route("/", handler);
        self
    }

    pub fn related(mut self, path: &str, handler: MethodRouter<S>) -> Self {
        self.router = self.router.route(path, handler);
        self
    }

    pub fn typed_routes(
        mut self,
        routes: &[&str],
        handler: MethodRouter<S>,
    ) -> Self {
        for route in routes {
            let route = format!("/{}", route);
            self.router = self.router.route(&route, handler.clone())
        }
        self
    }

    pub fn with_layer<L>(mut self, layer: L) -> Self
    where
        L: Layer<Route> + Clone + Send + Sync + 'static,
        L::Service: Service<Request> + Clone + Send + Sync + 'static,
        <L::Service as Service<Request>>::Response: IntoResponse + 'static,
        <L::Service as Service<Request>>::Error: Into<Infallible> + 'static,
        <L::Service as Service<Request>>::Future: Send + 'static,
    {
        self.router = self.router.layer(layer);
        self
    }

    pub fn with_prefix(mut self, prefix: &str) -> Self {
        self.prefix = Some(prefix.to_string());
        self
    }

    pub fn build(self) -> (String, Router<S>) {
        let path = match self.prefix {
            Some(prefix) => Self::prefixed_route(&prefix, &self.base_path),
            None => self.base_path,
        };
        (path, self.router)
    }

    fn safe_path(path: &str) -> String {
        if path.starts_with("/") {
            path.to_string()
        } else {
            format!("/{}", path)
        }
    }

    fn prefixed_route(prefix: &str, route: &str) -> String {
        if route.starts_with('/') {
            format!("{prefix}/{}", route.trim_start_matches('/'))
        } else {
            format!("{prefix}/{route}")
        }
    }
}