scuffle_http/service/
function.rs

1use std::fmt::Debug;
2use std::future::Future;
3use std::net::SocketAddr;
4
5use super::{HttpService, HttpServiceFactory};
6use crate::IncomingRequest;
7
8/// A [`HttpService`] that is created from a function.
9///
10/// The given function will be called for each incoming request.
11/// This is useful for creating simple services without needing to implement the [`HttpService`] trait.
12///
13/// Create by calling [`fn_http_service`].
14#[derive(Clone)]
15pub struct FnHttpService<F>(F);
16
17impl<F> Debug for FnHttpService<F> {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        f.debug_tuple("FnHttpService").field(&std::any::type_name::<F>()).finish()
20    }
21}
22
23/// Create a [`FnHttpService`] from a given function.
24///
25/// See [`FnHttpService`] for details.
26pub fn fn_http_service<F, Fut, E, B>(f: F) -> FnHttpService<F>
27where
28    F: Fn(IncomingRequest) -> Fut,
29    Fut: Future<Output = Result<http::Response<B>, E>> + Send,
30    E: std::error::Error,
31    B: http_body::Body,
32{
33    FnHttpService(f)
34}
35
36impl<F, Fut, E, B> HttpService for FnHttpService<F>
37where
38    F: Fn(IncomingRequest) -> Fut,
39    Fut: Future<Output = Result<http::Response<B>, E>> + Send,
40    E: std::error::Error,
41    B: http_body::Body,
42{
43    type Error = E;
44    type ResBody = B;
45
46    fn call(
47        &mut self,
48        req: IncomingRequest,
49    ) -> impl Future<Output = Result<http::Response<Self::ResBody>, Self::Error>> + Send {
50        (self.0)(req)
51    }
52}
53
54/// A [`HttpServiceFactory`] that creates a [`FnHttpService`] from a function.
55///
56/// The given function will be called for each new connection.
57/// This is useful for creating simple factories without needing to implement the [`HttpServiceFactory`] trait.
58///
59/// Create by calling [`fn_http_service_factory`].
60#[derive(Clone)]
61pub struct FnHttpServiceFactory<F>(F);
62
63impl<F> Debug for FnHttpServiceFactory<F> {
64    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        f.debug_tuple("FnHttpServiceFactory")
66            .field(&std::any::type_name::<F>())
67            .finish()
68    }
69}
70
71/// Create a [`FnHttpServiceFactory`] from a given function.
72///
73/// See [`FnHttpServiceFactory`] for details.
74pub fn fn_http_service_factory<F, Fut, E, S>(f: F) -> FnHttpServiceFactory<F>
75where
76    F: Fn(SocketAddr) -> Fut,
77    Fut: Future<Output = Result<S, E>> + Send,
78    E: std::error::Error,
79    S: HttpService,
80{
81    FnHttpServiceFactory(f)
82}
83
84impl<F, Fut, E, S> HttpServiceFactory for FnHttpServiceFactory<F>
85where
86    F: Fn(SocketAddr) -> Fut,
87    Fut: Future<Output = Result<S, E>> + Send,
88    E: std::error::Error,
89    S: HttpService,
90{
91    type Error = E;
92    type Service = S;
93
94    fn new_service(&mut self, remote_addr: SocketAddr) -> impl Future<Output = Result<Self::Service, Self::Error>> + Send {
95        (self.0)(remote_addr)
96    }
97}
98
99#[cfg(test)]
100#[cfg_attr(all(test, coverage_nightly), coverage(off))]
101mod tests {
102    use std::convert::Infallible;
103
104    #[test]
105    fn fn_service_debug() {
106        let service = super::fn_http_service(|_| async { Ok::<_, Infallible>(http::Response::new(String::new())) });
107        assert_eq!(
108            format!("{service:?}"),
109            "FnHttpService(\"scuffle_http::service::function::tests::fn_service_debug::{{closure}}\")"
110        );
111    }
112
113    #[test]
114    fn fn_service_factory_debug() {
115        let factory = super::fn_http_service_factory(|_| async {
116            Ok::<_, Infallible>(super::fn_http_service(|_| async {
117                Ok::<_, Infallible>(http::Response::new(String::new()))
118            }))
119        });
120        assert_eq!(
121            format!("{factory:?}"),
122            "FnHttpServiceFactory(\"scuffle_http::service::function::tests::fn_service_factory_debug::{{closure}}\")"
123        );
124    }
125}