Skip to main content

typeway_server/
effects.rs

1//! Server-side middleware effects tracking.
2//!
3//! [`EffectfulServer`] wraps a [`Server`](crate::server::Server) and tracks which middleware effects
4//! have been provided via `.provide::<E>()`. The `.serve()` method only
5//! compiles when all effects declared in the API type (via
6//! [`Requires<E, _>`](typeway_core::effects::Requires)) have been discharged.
7//!
8//! # Example
9//!
10//! ```ignore
11//! use typeway_core::effects::*;
12//!
13//! type API = (
14//!     Requires<AuthRequired, GetEndpoint<UserPath, User>>,
15//!     Requires<CorsRequired, GetEndpoint<PublicPath, Data>>,
16//!     GetEndpoint<HealthPath, String>,
17//! );
18//!
19//! EffectfulServer::<API>::new(handlers)
20//!     .provide::<AuthRequired>()
21//!     .layer(auth_layer)
22//!     .provide::<CorsRequired>()
23//!     .layer(CorsLayer::permissive())
24//!     .serve(addr)   // only compiles because both effects are provided
25//!     .await;
26//! ```
27
28use std::convert::Infallible;
29use std::future::Future;
30use std::marker::PhantomData;
31use std::net::SocketAddr;
32use std::sync::Arc;
33
34use typeway_core::effects::{AllProvided, ECons, ENil, Effect};
35use typeway_core::ApiSpec;
36
37use crate::body::BoxBody;
38use crate::router::{Router, RouterService};
39use crate::server::LayeredServer;
40use crate::serves::Serves;
41
42/// A server builder that tracks which middleware effects have been provided.
43///
44/// The `Provided` type parameter is a type-level list of effects that have
45/// been discharged via `.provide::<E>()`. It starts as [`ENil`] and grows
46/// with each `.provide()` call.
47///
48/// The `.serve()` method requires `A: AllProvided<Provided, _>`, ensuring
49/// that every [`Requires<E, _>`](typeway_core::effects::Requires) in the
50/// API type has a corresponding `.provide::<E>()`.
51pub struct EffectfulServer<A: ApiSpec, Provided = ENil> {
52    router: Arc<Router>,
53    _api: PhantomData<A>,
54    _provided: PhantomData<Provided>,
55}
56
57impl<A: ApiSpec> EffectfulServer<A, ENil> {
58    /// Create an effectful server from a handler tuple.
59    ///
60    /// The handler tuple must cover every endpoint in the API, just like
61    /// [`Server::new`](crate::server::Server::new).
62    pub fn new<H: Serves<A>>(handlers: H) -> Self {
63        let mut router = Router::new();
64        handlers.register(&mut router);
65        EffectfulServer {
66            router: Arc::new(router),
67            _api: PhantomData,
68            _provided: PhantomData,
69        }
70    }
71}
72
73impl<A: ApiSpec, P> EffectfulServer<A, P> {
74    /// Declare that a middleware effect has been provided.
75    ///
76    /// Each `.provide::<E>()` call adds `E` to the type-level list of
77    /// provided effects. Pair this with a `.layer()` call that applies
78    /// the actual middleware.
79    ///
80    /// # Example
81    ///
82    /// ```ignore
83    /// server
84    ///     .provide::<AuthRequired>()
85    ///     .layer(auth_layer)
86    /// ```
87    pub fn provide<E: Effect>(self) -> EffectfulServer<A, ECons<E, P>> {
88        EffectfulServer {
89            router: self.router,
90            _api: PhantomData,
91            _provided: PhantomData,
92        }
93    }
94
95    /// Set a path prefix for all routes.
96    pub fn nest(self, prefix: &str) -> Self {
97        self.router.set_prefix(prefix);
98        self
99    }
100
101    /// Set the maximum request body size in bytes.
102    pub fn max_body_size(self, max: usize) -> Self {
103        self.router.set_max_body_size(max);
104        self
105    }
106
107    /// Add shared state accessible via [`State<T>`](crate::extract::State) extractors.
108    pub fn with_state<T: Clone + Send + Sync + 'static>(self, state: T) -> Self {
109        self.router.set_state_injector(Arc::new(move |ext| {
110            ext.insert(state.clone());
111        }));
112        self
113    }
114
115    /// Apply a Tower middleware layer to the server.
116    ///
117    /// The layer wraps the entire router service. This is typically paired
118    /// with a `.provide::<E>()` call to discharge an effect requirement.
119    ///
120    /// # Example
121    ///
122    /// ```ignore
123    /// server
124    ///     .provide::<CorsRequired>()
125    ///     .layer(CorsLayer::permissive())
126    /// ```
127    pub fn layer<L>(self, layer: L) -> EffectfulLayeredServer<A, P, L::Service>
128    where
129        L: tower_layer::Layer<RouterService>,
130        L::Service: tower_service::Service<
131                http::Request<hyper::body::Incoming>,
132                Response = http::Response<BoxBody>,
133                Error = Infallible,
134            > + Clone
135            + Send
136            + 'static,
137        <L::Service as tower_service::Service<http::Request<hyper::body::Incoming>>>::Future:
138            Send + 'static,
139    {
140        let router = self.router.clone();
141        let svc = RouterService::new(self.router);
142        let layered = layer.layer(svc);
143        EffectfulLayeredServer {
144            service: layered,
145            router,
146            _api: PhantomData,
147            _provided: PhantomData,
148        }
149    }
150
151    /// Finalize the server and convert to a regular [`Server`](crate::server::Server).
152    ///
153    /// Only compiles if all required effects have been provided.
154    pub fn ready<Idx>(self) -> crate::server::Server<A>
155    where
156        A: AllProvided<P, Idx>,
157    {
158        crate::server::Server::from_router(self.router)
159    }
160
161    /// Start serving HTTP requests on the given address.
162    ///
163    /// Only compiles if all required effects have been provided via
164    /// `.provide::<E>()` calls.
165    pub async fn serve<Idx>(
166        self,
167        addr: SocketAddr,
168    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
169    where
170        A: AllProvided<P, Idx>,
171    {
172        self.ready::<Idx>().serve(addr).await
173    }
174
175    /// Start serving with graceful shutdown.
176    ///
177    /// Only compiles if all required effects have been provided.
178    pub async fn serve_with_shutdown<Idx>(
179        self,
180        listener: tokio::net::TcpListener,
181        shutdown: impl Future<Output = ()> + Send,
182    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
183    where
184        A: AllProvided<P, Idx>,
185    {
186        self.ready::<Idx>()
187            .serve_with_shutdown(listener, shutdown)
188            .await
189    }
190}
191
192/// An effectful server with Tower middleware layers applied.
193///
194/// Created by [`EffectfulServer::layer`]. Supports further `.provide()`,
195/// `.layer()`, and `.serve()` calls.
196pub struct EffectfulLayeredServer<A: ApiSpec, Provided, S> {
197    service: S,
198    router: Arc<Router>,
199    _api: PhantomData<A>,
200    _provided: PhantomData<Provided>,
201}
202
203impl<A: ApiSpec, P, S> EffectfulLayeredServer<A, P, S> {
204    /// Declare that a middleware effect has been provided.
205    pub fn provide<E: Effect>(self) -> EffectfulLayeredServer<A, ECons<E, P>, S> {
206        EffectfulLayeredServer {
207            service: self.service,
208            router: self.router,
209            _api: PhantomData,
210            _provided: PhantomData,
211        }
212    }
213
214    /// Add shared state accessible via [`State<T>`](crate::extract::State) extractors.
215    pub fn with_state<T: Clone + Send + Sync + 'static>(self, state: T) -> Self {
216        self.router.set_state_injector(Arc::new(move |ext| {
217            ext.insert(state.clone());
218        }));
219        self
220    }
221
222    /// Set the maximum request body size.
223    pub fn max_body_size(self, max: usize) -> Self {
224        self.router.set_max_body_size(max);
225        self
226    }
227
228    /// Set a path prefix for all routes.
229    pub fn nest(self, prefix: &str) -> Self {
230        self.router.set_prefix(prefix);
231        self
232    }
233}
234
235impl<A: ApiSpec, P, S> EffectfulLayeredServer<A, P, S>
236where
237    S: tower_service::Service<
238            http::Request<hyper::body::Incoming>,
239            Response = http::Response<BoxBody>,
240            Error = Infallible,
241        > + Clone
242        + Send
243        + 'static,
244    S::Future: Send + 'static,
245{
246    /// Apply another Tower middleware layer.
247    pub fn layer<L>(self, layer: L) -> EffectfulLayeredServer<A, P, L::Service>
248    where
249        L: tower_layer::Layer<S>,
250        L::Service: tower_service::Service<
251                http::Request<hyper::body::Incoming>,
252                Response = http::Response<BoxBody>,
253                Error = Infallible,
254            > + Clone
255            + Send
256            + 'static,
257        <L::Service as tower_service::Service<http::Request<hyper::body::Incoming>>>::Future:
258            Send + 'static,
259    {
260        EffectfulLayeredServer {
261            service: layer.layer(self.service),
262            router: self.router,
263            _api: PhantomData,
264            _provided: PhantomData,
265        }
266    }
267}
268
269impl<A: ApiSpec, P, S> EffectfulLayeredServer<A, P, S>
270where
271    S: tower_service::Service<
272            http::Request<hyper::body::Incoming>,
273            Response = http::Response<BoxBody>,
274            Error = Infallible,
275        > + Clone
276        + Send
277        + 'static,
278    S::Future: Send + 'static,
279{
280    /// Finalize into a [`LayeredServer`].
281    ///
282    /// Only compiles if all required effects have been provided.
283    pub fn ready<Idx>(self) -> LayeredServer<S>
284    where
285        A: AllProvided<P, Idx>,
286    {
287        LayeredServer {
288            service: self.service,
289            router: self.router,
290        }
291    }
292
293    /// Start serving HTTP requests.
294    ///
295    /// Only compiles if all required effects have been provided.
296    pub async fn serve<Idx>(
297        self,
298        addr: SocketAddr,
299    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
300    where
301        A: AllProvided<P, Idx>,
302    {
303        self.ready::<Idx>().serve(addr).await
304    }
305
306    /// Start serving with graceful shutdown.
307    ///
308    /// Only compiles if all required effects have been provided.
309    pub async fn serve_with_shutdown<Idx>(
310        self,
311        listener: tokio::net::TcpListener,
312        shutdown: impl Future<Output = ()> + Send,
313    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
314    where
315        A: AllProvided<P, Idx>,
316    {
317        self.ready::<Idx>()
318            .serve_with_shutdown(listener, shutdown)
319            .await
320    }
321}