cot/
handler.rs

1use std::future::Future;
2
3use async_trait::async_trait;
4use tower::util::BoxCloneSyncService;
5
6use crate::error::ErrorRepr;
7use crate::request::Request;
8use crate::response::{not_found_response, Response};
9use crate::{Error, Result};
10
11/// A function that takes a request and returns a response.
12///
13/// This is the main building block of a Cot app. You shouldn't
14/// usually need to implement this directly, as it is already
15/// implemented for closures and functions that take a [`Request`]
16/// and return a [`Result<Response>`].
17#[async_trait]
18pub trait RequestHandler {
19    /// Handle the request and returns a response.
20    ///
21    /// # Errors
22    ///
23    /// This method can return an error if the request handler fails to handle
24    /// the request.
25    async fn handle(&self, request: Request) -> Result<Response>;
26}
27
28#[async_trait]
29impl<T, R> RequestHandler for T
30where
31    T: Fn(Request) -> R + Clone + Send + Sync + 'static,
32    R: for<'a> Future<Output = Result<Response>> + Send,
33{
34    async fn handle(&self, request: Request) -> Result<Response> {
35        let response = self(request).await;
36        match response {
37            Ok(response) => Ok(response),
38            Err(error) => match error.inner {
39                ErrorRepr::NotFound { message } => Ok(not_found_response(message)),
40                _ => Err(error),
41            },
42        }
43    }
44}
45
46/// A wrapper around a handler that's used in
47/// [`Bootstrapper`](cot::Bootstrapper).
48///
49/// It is returned by
50/// [`Bootstrapper::into_context_and_handler`](cot::Bootstrapper::into_context_and_handler).
51/// Typically, you don't need to interact with this type directly, except for
52/// creating it in [`Project::middlewares`](cot::Project::middlewares) through
53/// the [`RootHandlerBuilder::build`](cot::project::RootHandlerBuilder::build).
54/// method.
55///
56/// # Examples
57///
58/// ```
59/// use cot::config::ProjectConfig;
60/// use cot::project::{RootHandlerBuilder, WithApps};
61/// use cot::static_files::StaticFilesMiddleware;
62/// use cot::{Bootstrapper, BoxedHandler, Project, ProjectContext};
63///
64/// struct MyProject;
65/// impl Project for MyProject {
66///     fn middlewares(
67///         &self,
68///         handler: RootHandlerBuilder,
69///         context: &ProjectContext<WithApps>,
70///     ) -> BoxedHandler {
71///         handler
72///             .middleware(StaticFilesMiddleware::from_context(context))
73///             .build()
74///     }
75/// }
76///
77/// # #[tokio::main]
78/// # async fn main() -> cot::Result<()> {
79/// let bootstrapper = Bootstrapper::new(MyProject)
80///     .with_config(ProjectConfig::default())
81///     .boot()
82///     .await?;
83/// let (context, handler) = bootstrapper.into_context_and_handler();
84/// # Ok(())
85/// # }
86/// ```
87pub type BoxedHandler = BoxCloneSyncService<Request, Response, Error>;