summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2025-10-27 18:15:25 -0400
committerOwen Jacobson <owen@grimoire.ca>2025-10-28 01:43:26 -0400
commitd66728889105f6f1ef5113d9ceb223e362df0008 (patch)
tree5102813fbaa222276dcabd15a736838f9641d71f
parentc2a3a010c67776b9a459d7ba0930630ff25a3a51 (diff)
Convert the `Setup` component into a freestanding struct.
The changes to the setup-requiring middleware are probably more general than was strictly needed, but they will make it work with anything that can provide a `Setup` component rather than being bolted to `App` specifically, which feels tidier.
-rw-r--r--src/app.rs10
-rw-r--r--src/setup/app.rs13
-rw-r--r--src/setup/handlers/setup/mod.rs14
-rw-r--r--src/setup/handlers/setup/test.rs6
-rw-r--r--src/setup/required.rs31
-rw-r--r--src/ui/handlers/setup.rs7
6 files changed, 48 insertions, 33 deletions
diff --git a/src/app.rs b/src/app.rs
index 74e1070..202d542 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -58,8 +58,8 @@ impl App {
Messages::new(self.db.clone(), self.events.clone())
}
- pub const fn setup(&self) -> Setup<'_> {
- Setup::new(&self.db, &self.events)
+ pub fn setup(&self) -> Setup {
+ Setup::new(self.db.clone(), self.events.clone())
}
pub const fn tokens(&self) -> Tokens<'_> {
@@ -101,3 +101,9 @@ impl FromRef<App> for Messages {
app.messages()
}
}
+
+impl FromRef<App> for Setup {
+ fn from_ref(app: &App) -> Self {
+ app.setup()
+ }
+}
diff --git a/src/setup/app.rs b/src/setup/app.rs
index 2a8ec30..9539406 100644
--- a/src/setup/app.rs
+++ b/src/setup/app.rs
@@ -10,13 +10,14 @@ use crate::{
user::create::{self, Create},
};
-pub struct Setup<'a> {
- db: &'a SqlitePool,
- events: &'a Broadcaster,
+#[derive(Clone)]
+pub struct Setup {
+ db: SqlitePool,
+ events: Broadcaster,
}
-impl<'a> Setup<'a> {
- pub const fn new(db: &'a SqlitePool, events: &'a Broadcaster) -> Self {
+impl Setup {
+ pub const fn new(db: SqlitePool, events: Broadcaster) -> Self {
Self { db, events }
}
@@ -41,7 +42,7 @@ impl<'a> Setup<'a> {
tx.tokens().create(&token, &secret).await?;
tx.commit().await?;
- stored.publish(self.events);
+ stored.publish(&self.events);
Ok(secret)
}
diff --git a/src/setup/handlers/setup/mod.rs b/src/setup/handlers/setup/mod.rs
index fe24798..2977da8 100644
--- a/src/setup/handlers/setup/mod.rs
+++ b/src/setup/handlers/setup/mod.rs
@@ -5,21 +5,25 @@ use axum::{
};
use crate::{
- app::App, clock::RequestedAt, empty::Empty, error::Internal, name::Name, password::Password,
- setup::app, token::extract::IdentityCookie,
+ clock::RequestedAt,
+ empty::Empty,
+ error::Internal,
+ name::Name,
+ password::Password,
+ setup::{app, app::Setup},
+ token::extract::IdentityCookie,
};
#[cfg(test)]
mod test;
pub async fn handler(
- State(app): State<App>,
+ State(setup): State<Setup>,
RequestedAt(setup_at): RequestedAt,
identity: IdentityCookie,
Json(request): Json<Request>,
) -> Result<(IdentityCookie, Empty), Error> {
- let secret = app
- .setup()
+ let secret = setup
.initial(&request.name, &request.password, &setup_at)
.await
.map_err(Error)?;
diff --git a/src/setup/handlers/setup/test.rs b/src/setup/handlers/setup/test.rs
index 283fe8b..670c111 100644
--- a/src/setup/handlers/setup/test.rs
+++ b/src/setup/handlers/setup/test.rs
@@ -20,7 +20,7 @@ async fn fresh_instance() {
password: password.clone(),
};
let (identity, Empty) =
- super::handler(State(app.clone()), fixtures::now(), identity, Json(request))
+ super::handler(State(app.setup()), fixtures::now(), identity, Json(request))
.await
.expect("setup in a fresh app succeeds");
@@ -43,7 +43,7 @@ async fn login_exists() {
let (name, password) = fixtures::user::propose();
let request = super::Request { name, password };
let super::Error(error) =
- super::handler(State(app.clone()), fixtures::now(), identity, Json(request))
+ super::handler(State(app.setup()), fixtures::now(), identity, Json(request))
.await
.expect_err("setup in a populated app fails");
@@ -68,7 +68,7 @@ async fn invalid_name() {
password: password.clone(),
};
let super::Error(error) =
- super::handler(State(app.clone()), fixtures::now(), identity, Json(request))
+ super::handler(State(app.setup()), fixtures::now(), identity, Json(request))
.await
.expect_err("setup with an invalid name fails");
diff --git a/src/setup/required.rs b/src/setup/required.rs
index a2aed18..e475381 100644
--- a/src/setup/required.rs
+++ b/src/setup/required.rs
@@ -4,26 +4,29 @@ use std::{
};
use axum::{
- extract::Request,
+ extract::{FromRef, Request},
http::StatusCode,
response::{IntoResponse, Response},
};
use tower::{Layer, Service};
-use crate::{app::App, error::Internal};
+use crate::{error::Internal, setup::app::Setup};
#[derive(Clone)]
-pub struct Required(pub App);
+pub struct Required<App>(pub App);
-impl Required {
- pub fn with_fallback<F>(self, fallback: F) -> WithFallback<F> {
+impl<App> Required<App> {
+ pub fn with_fallback<F>(self, fallback: F) -> WithFallback<App, F> {
let Self(app) = self;
WithFallback { app, fallback }
}
}
-impl<S> Layer<S> for Required {
- type Service = Middleware<S, Unavailable>;
+impl<S, App> Layer<S> for Required<App>
+where
+ Self: Clone,
+{
+ type Service = Middleware<S, App, Unavailable>;
fn layer(&self, inner: S) -> Self::Service {
let Self(app) = self.clone();
@@ -36,16 +39,16 @@ impl<S> Layer<S> for Required {
}
#[derive(Clone)]
-pub struct WithFallback<F> {
+pub struct WithFallback<App, F> {
app: App,
fallback: F,
}
-impl<S, F> Layer<S> for WithFallback<F>
+impl<S, App, F> Layer<S> for WithFallback<App, F>
where
Self: Clone,
{
- type Service = Middleware<S, F>;
+ type Service = Middleware<S, App, F>;
fn layer(&self, inner: S) -> Self::Service {
let Self { app, fallback } = self.clone();
@@ -58,17 +61,19 @@ where
}
#[derive(Clone)]
-pub struct Middleware<S, F> {
+pub struct Middleware<S, App, F> {
inner: S,
app: App,
fallback: F,
}
-impl<S, F> Service<Request> for Middleware<S, F>
+impl<S, App, F> Service<Request> for Middleware<S, App, F>
where
+ Setup: FromRef<App>,
Self: Clone,
S: Service<Request, Response = Response> + Send + 'static,
S::Future: Send,
+ App: Send + 'static,
F: IntoResponse + Clone + Send + 'static,
{
type Response = S::Response;
@@ -87,7 +92,7 @@ where
} = self.clone();
Box::pin(async move {
- match app.setup().completed().await {
+ match Setup::from_ref(&app).completed().await {
Ok(true) => inner.call(req).await,
Ok(false) => Ok(fallback.into_response()),
Err(error) => Ok(Internal::from(error).into_response()),
diff --git a/src/ui/handlers/setup.rs b/src/ui/handlers/setup.rs
index 49821cf..5707765 100644
--- a/src/ui/handlers/setup.rs
+++ b/src/ui/handlers/setup.rs
@@ -4,14 +4,13 @@ use axum::{
};
use crate::{
- app::App,
error::Internal,
+ setup::app::Setup,
ui::assets::{Asset, Assets},
};
-pub async fn handler(State(app): State<App>) -> Result<Asset, Error> {
- if app
- .setup()
+pub async fn handler(State(setup): State<Setup>) -> Result<Asset, Error> {
+ if setup
.completed()
.await
.map_err(Internal::from)