diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2025-10-27 17:07:53 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2025-10-28 01:27:13 -0400 |
| commit | 2d05e6fb933d8c33078232b3bdfbc2aa05106d80 (patch) | |
| tree | 7f5b0c3d4272edeabeb52538fa7ed3090d6bccd1 /src/app.rs | |
| parent | 58e6496558a01052537c5272169aea3e79ccbc8e (diff) | |
Make `Boot` a freestanding app type, rather than a view of `crate::app::App`'s internals.
In the course of working on web push, I determined that we probably need to make `App` generic over the web push client we're using, so that tests can use a dummy client while the real app uses a client created at startup and maintained over the life of the program's execution. The most direct implementation of that is to render App as `App<P>`, where the parameter is occupied by the specific web push client type in use. However, doing this requires refactoring at _every_ site that mentions `App`, including every handler, even though the vast majority of those sites will not be concerned with web push.
I reviewed a few options with @wlonk:
* Accept the type parameter and apply it everywhere, as the cost of supporting web push.
* Hard-code the use of a specific web push client.
* Insulate handlers &c from `App` via provider traits, mimicing what we do for repository provider traits today.
* Treat each app type as a freestanding state in its own right, so that only push-related components need to consider push clients (as far as is feasible).
This is a prototype towards that last point, using a simple app component (boot) as a testbed. `FromRef` allows handlers that take a `Boot` to be used in routes that provide an `App`, so this is a contained change. However, the structure of `FromRef` prevents `Boot` from carrying any lifetime narrower than `'static`, so it now holds clones of the state fields it acquires from App, instead of references. This is fine - that's just a database pool, and sqlx's pool type is designed to be shared via cloning. From <https://docs.rs/sqlx/latest/sqlx/struct.Pool.html>:
> Cloning Pool is cheap as it is simply a reference-counted handle to the inner pool state.
Diffstat (limited to 'src/app.rs')
| -rw-r--r-- | src/app.rs | 11 |
1 files changed, 9 insertions, 2 deletions
@@ -1,3 +1,4 @@ +use axum::extract::FromRef; use sqlx::sqlite::SqlitePool; #[cfg(test)] @@ -33,8 +34,8 @@ impl App { } impl App { - pub const fn boot(&self) -> Boot<'_> { - Boot::new(&self.db) + pub fn boot(&self) -> Boot { + Boot::new(self.db.clone()) } pub const fn conversations(&self) -> Conversations<'_> { @@ -70,3 +71,9 @@ impl App { Users::new(&self.db, &self.events) } } + +impl FromRef<App> for Boot { + fn from_ref(app: &App) -> Self { + app.boot() + } +} |
