Improve compile times of handle_error and check_infallible (#198)

* Improve compile times of `handle_error`

This brings the compile time of the example posted [here][example] from
3 seconds down to 0.3 seconds for me.

Having the bounds on the methods does improve UX but not worth
sacrificing 10x compile time for.

[example]: https://github.com/tokio-rs/axum/issues/145#issue-963183256

* Improve compile time of `check_infallible`

* update changelog
This commit is contained in:
David Pedersen 2021-08-17 19:07:47 +02:00 committed by GitHub
parent 93cdfe8c5f
commit dd0c345040
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 39 additions and 22 deletions

View file

@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
# Unreleased # Unreleased
- Overall compile time improvements. If you're having issues with compile time
please file an issue!
- Make `FromRequest` default to being generic over `body::Body` ([#146](https://github.com/tokio-rs/axum/pull/146)) - Make `FromRequest` default to being generic over `body::Body` ([#146](https://github.com/tokio-rs/axum/pull/146))
- Implement `std::error::Error` for all rejections ([#153](https://github.com/tokio-rs/axum/pull/153)) - Implement `std::error::Error` for all rejections ([#153](https://github.com/tokio-rs/axum/pull/153))
- Add `RoutingDsl::or` for combining routes ([#108](https://github.com/tokio-rs/axum/pull/108)) - Add `RoutingDsl::or` for combining routes ([#108](https://github.com/tokio-rs/axum/pull/108))
@ -39,6 +41,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `ServiceExt` has been removed and its methods have been moved to `RoutingDsl` ([#160](https://github.com/tokio-rs/axum/pull/160)) - `ServiceExt` has been removed and its methods have been moved to `RoutingDsl` ([#160](https://github.com/tokio-rs/axum/pull/160))
- `extractor_middleware` now requires `RequestBody: Default` ([#167](https://github.com/tokio-rs/axum/pull/167)) - `extractor_middleware` now requires `RequestBody: Default` ([#167](https://github.com/tokio-rs/axum/pull/167))
- Convert `RequestAlreadyExtracted` to an enum with each possible error variant ([#167](https://github.com/tokio-rs/axum/pull/167)) - Convert `RequestAlreadyExtracted` to an enum with each possible error variant ([#167](https://github.com/tokio-rs/axum/pull/167))
- `RoutingDsl::check_infallible` now returns a `CheckInfallible` service. This
is to improve compile times.
- These future types have been moved - These future types have been moved
- `extract::extractor_middleware::ExtractorMiddlewareResponseFuture` moved - `extract::extractor_middleware::ExtractorMiddlewareResponseFuture` moved
to `extract::extractor_middleware::future::ResponseFuture` ([#133](https://github.com/tokio-rs/axum/pull/133)) to `extract::extractor_middleware::future::ResponseFuture` ([#133](https://github.com/tokio-rs/axum/pull/133))

View file

@ -8,7 +8,6 @@ use crate::{
connect_info::{Connected, IntoMakeServiceWithConnectInfo}, connect_info::{Connected, IntoMakeServiceWithConnectInfo},
NestedUri, NestedUri,
}, },
response::IntoResponse,
service::{HandleError, HandleErrorFromRouter}, service::{HandleError, HandleErrorFromRouter},
util::ByteStr, util::ByteStr,
}; };
@ -416,28 +415,18 @@ pub trait RoutingDsl: crate::sealed::Sealed + Sized {
/// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # }; /// # };
/// ``` /// ```
fn handle_error<ReqBody, ResBody, F, Res, E>( fn handle_error<ReqBody, F>(
self, self,
f: F, f: F,
) -> HandleError<Self, F, ReqBody, HandleErrorFromRouter> ) -> HandleError<Self, F, ReqBody, HandleErrorFromRouter> {
where
Self: Service<Request<ReqBody>, Response = Response<ResBody>>,
F: FnOnce(Self::Error) -> Result<Res, E>,
Res: IntoResponse,
ResBody: http_body::Body<Data = Bytes> + Send + Sync + 'static,
ResBody::Error: Into<BoxError> + Send + Sync + 'static,
{
HandleError::new(self, f) HandleError::new(self, f)
} }
/// Check that your service cannot fail. /// Check that your service cannot fail.
/// ///
/// That is, its error type is [`Infallible`]. /// That is, its error type is [`Infallible`].
fn check_infallible<ReqBody>(self) -> Self fn check_infallible(self) -> CheckInfallible<Self> {
where CheckInfallible(self)
Self: Service<Request<ReqBody>, Error = Infallible>,
{
self
} }
} }
@ -964,6 +953,35 @@ fn strip_prefix(uri: &Uri, prefix: &str) -> Uri {
Uri::from_parts(parts).unwrap() Uri::from_parts(parts).unwrap()
} }
/// Middleware that statically verifies that a service cannot fail.
///
/// Created with [`check_infallible`](RoutingDsl::check_infallible).
#[derive(Debug, Clone, Copy)]
pub struct CheckInfallible<S>(S);
impl<R, S> Service<R> for CheckInfallible<S>
where
S: Service<R, Error = Infallible>,
{
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
#[inline]
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.0.poll_ready(cx)
}
#[inline]
fn call(&mut self, req: R) -> Self::Future {
self.0.call(req)
}
}
impl<S> RoutingDsl for CheckInfallible<S> {}
impl<S> crate::sealed::Sealed for CheckInfallible<S> {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -453,15 +453,10 @@ impl<S, F, B> OnMethod<S, F, B> {
/// details. /// details.
/// ///
/// [`RoutingDsl::handle_error`]: crate::routing::RoutingDsl::handle_error /// [`RoutingDsl::handle_error`]: crate::routing::RoutingDsl::handle_error
pub fn handle_error<ReqBody, H, Res, E>( pub fn handle_error<ReqBody, H>(
self, self,
f: H, f: H,
) -> HandleError<Self, H, ReqBody, HandleErrorFromService> ) -> HandleError<Self, H, ReqBody, HandleErrorFromService> {
where
Self: Service<Request<ReqBody>, Response = Response<BoxBody>>,
H: FnOnce(<Self as Service<Request<ReqBody>>>::Error) -> Result<Res, E>,
Res: IntoResponse,
{
HandleError::new(self, f) HandleError::new(self, f)
} }
} }