use axum::{ extract::Request, http::StatusCode, response::{IntoResponse, Response}, }; use std::{ pin::Pin, task::{Context, Poll}, }; use tower::{Layer, Service}; use crate::{app::App, error::Internal}; #[derive(Clone)] pub struct Required(pub App); impl Required { pub fn with_fallback(self, fallback: F) -> WithFallback { let Self(app) = self; WithFallback { app, fallback } } } impl Layer for Required { type Service = Middleware; fn layer(&self, inner: S) -> Self::Service { let Self(app) = self.clone(); Middleware { inner, app, fallback: Unavailable, } } } #[derive(Clone)] pub struct WithFallback { app: App, fallback: F, } impl Layer for WithFallback where Self: Clone, { type Service = Middleware; fn layer(&self, inner: S) -> Self::Service { let Self { app, fallback } = self.clone(); Middleware { inner, app, fallback, } } } #[derive(Clone)] pub struct Middleware { inner: S, app: App, fallback: F, } impl Service for Middleware where Self: Clone, S: Service + Send + 'static, S::Future: Send, F: IntoResponse + Clone + Send + 'static, { type Response = S::Response; type Error = S::Error; type Future = Pin> + Send>>; fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { self.inner.poll_ready(ctx) } fn call(&mut self, req: Request) -> Self::Future { let Self { mut inner, app, fallback, } = self.clone(); Box::pin(async move { match app.setup().completed().await { Ok(true) => inner.call(req).await, Ok(false) => Ok(fallback.into_response()), Err(error) => Ok(Internal::from(error).into_response()), } }) } } #[derive(Clone)] pub struct Unavailable; impl IntoResponse for Unavailable { fn into_response(self) -> Response { ( StatusCode::SERVICE_UNAVAILABLE, "initial setup not completed", ) .into_response() } }