diff --git a/Cargo.toml b/Cargo.toml index 46190dd..4f0e049 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,8 @@ multiple_crate_versions = { level = "allow" } # otherwise there's too much correctness = { level = "deny", priority = -1 } nursery = { level = "deny", priority = -1 } +option_if_let_else = { level = "allow" } # I personally sometimes prefer what this prevents + pedantic = { level = "deny", priority = -1 } perf = { level = "deny", priority = -1 } style = { level = "deny", priority = -1 } diff --git a/src/server/mod.rs b/src/server/mod.rs index d52fbe0..278a221 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -4,6 +4,7 @@ use actix_web::{App, HttpServer, web}; pub mod caches; pub mod services; +pub mod static_app_data; pub struct AppState { pub args: crate::args::Args, @@ -11,6 +12,9 @@ pub struct AppState { pub cache: caches::AppCache<'static>, } +/// Type alias to be used just as `data: AppData,` extractor in [`actix_web`] request handlers. +pub type AppData = static_app_data::StaticAppDataExtractor<&'static AppState>; + /// Leaks memory for the sake of not atomic'ing all over. #[expect(clippy::missing_errors_doc)] pub async fn start_app(args: crate::args::Args, config: crate::conf::Config) -> io::Result<()> { @@ -18,10 +22,7 @@ pub async fn start_app(args: crate::args::Args, config: crate::conf::Config) -> let config = Box::leak(Box::new(config)); - let cache = caches::AppCache::new( - &config.unix.magic_paths, - &config.unix.groups, - ); + let cache = caches::AppCache::new(&config.unix.magic_paths, &config.unix.groups); let app: &AppState = Box::leak(Box::new(AppState { args, @@ -36,7 +37,7 @@ pub async fn start_app(args: crate::args::Args, config: crate::conf::Config) -> HttpServer::new(move || { App::new() - .app_data(web::Data::new(app)) + .app_data(app) .service(services::images::make_scope(ws::IMAGES)) .default_service(web::to(services::not_found::not_found)) }) diff --git a/src/server/services/images.rs b/src/server/services/images.rs index fa7873a..412c5aa 100644 --- a/src/server/services/images.rs +++ b/src/server/services/images.rs @@ -15,7 +15,7 @@ use actix_web::{ use crate::{ consts::{self, web_scopes as ws}, - server, + server::{self, AppData}, }; #[must_use] @@ -27,7 +27,7 @@ pub fn make_scope(path: &str) -> actix_web::Scope { #[get("/")] async fn get_default_image( - _data: web::Data<&server::AppState>, + data: AppData, _username: web::Path, ) -> HttpResponse { HttpResponse::Ok() @@ -41,7 +41,7 @@ async fn get_default_image( #[get("/{username}")] async fn get_image( - data: web::Data<&server::AppState>, + data: AppData, username: web::Path, ) -> web::Either { let cached_pfp = data.cache.get_pfp(username.to_string()).await; diff --git a/src/server/static_app_data.rs b/src/server/static_app_data.rs new file mode 100644 index 0000000..04b4881 --- /dev/null +++ b/src/server/static_app_data.rs @@ -0,0 +1,38 @@ +use std::{future::Ready, ops::Deref}; + +use actix_web::{error::InternalError, http::StatusCode}; + +/// App data extractor without any [`Arc`] inbetween, perefct if the app data is a simple +/// `&'static` reference that can be copied, no atomic anywhere. +pub struct StaticAppDataExtractor { + pub data: T, +} + +impl Deref for StaticAppDataExtractor { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.data + } +} + +impl actix_web::FromRequest for StaticAppDataExtractor { + type Error = InternalError<&'static str>; + type Future = Ready>; + + fn from_request( + req: &actix_web::HttpRequest, + _payload: &mut actix_web::dev::Payload, + ) -> Self::Future { + let val = match req.app_data::() { + Some(&data) => Ok(Self { data }), + None => Err(InternalError::new( + "Requested application data is not configured correctly. \ + View/enable debug logs for more details.", + StatusCode::INTERNAL_SERVER_ERROR, + )), + }; + + std::future::ready(val) + } +}