diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2025-06-17 01:22:54 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2025-06-17 01:22:54 -0400 |
| commit | 43375bcb875a31ce8c6132ce78552d45f64b261b (patch) | |
| tree | 6ea93d417f329baccaa84f1d557638f54f3f2d37 | |
| parent | 424fb08ecd315c67dd3862c29e87eea7bf32f65c (diff) | |
Use a fluent style for the middleware layers.
For endpoints that are unavailable, that default behaviour no longer needs to be specified: `Required(app)` will do that for you. For endpoints that are redirects until setup is completed, `Require(app).with_fallback(…response…)` will do that.
To make this a bit harder to break by accident, the default unavailable response is now its own type.
| -rw-r--r-- | src/cli.rs | 2 | ||||
| -rw-r--r-- | src/setup/mod.rs | 2 | ||||
| -rw-r--r-- | src/setup/required.rs | 53 | ||||
| -rw-r--r-- | src/ui/routes/mod.rs | 5 |
4 files changed, 39 insertions, 23 deletions
@@ -147,7 +147,7 @@ fn routers(app: &App) -> Router<App> { app.clone(), expire::middleware, )) - .route_layer(setup::Required::or_unavailable(app.clone())), + .route_layer(setup::Required(app.clone())), // API endpoints that handle setup setup::router(), // The UI (handles setup state itself) diff --git a/src/setup/mod.rs b/src/setup/mod.rs index 62972b3..a4b821c 100644 --- a/src/setup/mod.rs +++ b/src/setup/mod.rs @@ -3,4 +3,4 @@ pub mod repo; mod required; mod routes; -pub use self::{required::Layer as Required, routes::router}; +pub use self::{required::Required, routes::router}; diff --git a/src/setup/required.rs b/src/setup/required.rs index 5b7fe5b..2112e4b 100644 --- a/src/setup/required.rs +++ b/src/setup/required.rs @@ -5,34 +5,40 @@ use axum::{ }; use std::pin::Pin; use std::task::{Context, Poll}; -use tower::Service; +use tower::{Layer, Service}; use crate::{app::App, error::Internal}; -const UNAVAILABLE: (StatusCode, &str) = ( - StatusCode::SERVICE_UNAVAILABLE, - "initial setup not completed", -); - #[derive(Clone)] -pub struct Layer<F> { - app: App, - fallback: F, -} +pub struct Required(pub App); -impl Layer<(StatusCode, &'static str)> { - pub fn or_unavailable(app: App) -> Self { - Self::with_fallback(app, UNAVAILABLE) +impl Required { + pub fn with_fallback<F>(self, fallback: F) -> WithFallback<F> { + let Self(app) = self; + WithFallback { app, fallback } } } -impl<F> Layer<F> { - pub fn with_fallback(app: App, fallback: F) -> Self { - Layer { app, fallback } +impl<S> Layer<S> for Required { + type Service = Middleware<S, Unavailable>; + + fn layer(&self, inner: S) -> Self::Service { + let Self(app) = self.clone(); + Middleware { + inner, + app, + fallback: Unavailable, + } } } -impl<S, F> tower::Layer<S> for Layer<F> +#[derive(Clone)] +pub struct WithFallback<F> { + app: App, + fallback: F, +} + +impl<S, F> Layer<S> for WithFallback<F> where Self: Clone, { @@ -86,3 +92,16 @@ where }) } } + +#[derive(Clone)] +pub struct Unavailable; + +impl IntoResponse for Unavailable { + fn into_response(self) -> Response { + ( + StatusCode::SERVICE_UNAVAILABLE, + "initial setup not completed", + ) + .into_response() + } +} diff --git a/src/ui/routes/mod.rs b/src/ui/routes/mod.rs index 328eb73..dc94773 100644 --- a/src/ui/routes/mod.rs +++ b/src/ui/routes/mod.rs @@ -21,10 +21,7 @@ pub fn router(app: &App) -> Router<App> { .route("/login", get(login::get::handler)) .route("/ch/{channel}", get(ch::channel::get::handler)) .route("/invite/{invite}", get(invite::invite::get::handler)) - .route_layer(crate::setup::Required::with_fallback( - app.clone(), - Redirect::to("/setup"), - )), + .route_layer(crate::setup::Required(app.clone()).with_fallback(Redirect::to("/setup"))), ] .into_iter() .fold(Router::default(), Router::merge) |
