From c8aaa134aa3f39492ebf9fba840e644ffdd56f40 Mon Sep 17 00:00:00 2001 From: Nicolas Kamzol Date: Tue, 16 Sep 2025 11:12:49 +0200 Subject: [PATCH] build: disable S3 dependencies when `s3` feature is disabled This also makes the DIMSE backend mandatory for now. The crate feature `dimse` was removed as the project didn't compile without it. In a future version, we'll revisit this to make the DIMSE backend optional as well. This allows for use cases where users only need the S3 backend for example. --- Cargo.toml | 12 +++++------- src/backend/mod.rs | 34 ++++++++++++++-------------------- src/config/mod.rs | 19 ++++++++++--------- src/main.rs | 7 ------- 4 files changed, 29 insertions(+), 43 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fc640a4..c30deb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,10 +15,8 @@ license = "MIT" readme = "README.md" [features] -default = ["dimse", "s3"] -dimse = [] -# TODO: feature-gate S3 dependencies -s3 = [] +default = [] +s3 = ["dep:aws-config", "dep:aws-sdk-s3", "dep:aws-credential-types"] [dependencies] # DICOM processing @@ -54,9 +52,9 @@ multer = "3.1.0" pin-project = "1.1.10" image = { version = "0.25.6", features = ["png", "jpeg", "gif"] } # S3 backend -aws-config = { version = "1.6.2", features = ["behavior-version-latest"] } -aws-sdk-s3 = "1.85.0" -aws-credential-types = "1.2.3" +aws-config = { version = "1.6.2", features = ["behavior-version-latest"], optional = true } +aws-sdk-s3 = { version = "1.85.0", optional = true } +aws-credential-types = { version = "1.2.3", optional = true } [lints.rust] unsafe_code = "forbid" diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 0e5b9a9..8c907c9 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -1,20 +1,14 @@ use crate::api::qido::QidoService; use crate::api::stow::StowService; use crate::api::wado::WadoService; -use crate::backend::dimse::qido::DimseQidoService; -use crate::backend::dimse::stow::DimseStowService; -use crate::backend::dimse::wado::DimseWadoService; -use crate::backend::s3::wado::S3WadoService; use crate::config::BackendConfig; use crate::AppState; -use async_trait::async_trait; use axum::extract::{FromRef, FromRequestParts, Path}; use axum::http::request::Parts; use axum::http::StatusCode; use serde::Deserialize; use std::time::Duration; -// #[cfg(feature = "dimse")] pub mod dimse; #[cfg(feature = "s3")] @@ -54,8 +48,11 @@ where // TODO: Use a singleton to avoid re-creating on every request. let provider = match ae_config.backend { - #[cfg(feature = "dimse")] BackendConfig::Dimse { .. } => { + use crate::backend::dimse::qido::DimseQidoService; + use crate::backend::dimse::stow::DimseStowService; + use crate::backend::dimse::wado::DimseWadoService; + let pool = state.pools.get(&ae_config.aet).expect("pool should exist"); Self { @@ -75,19 +72,16 @@ where ))), } } - // For some reason serde doesn't work with feature-gated enum variants. - // A no-op backend is used as a workaround if the dimse feature is not enabled. - #[cfg(not(feature = "dimse"))] - Backend::Dimse => Self { - qido: None, - wado: None, - stow: None, - }, - BackendConfig::S3(config) => Self { - qido: None, - wado: Some(Box::new(S3WadoService::new(&config))), - stow: None, - }, + #[cfg(feature = "s3")] + BackendConfig::S3(config) => { + use crate::backend::s3::wado::S3WadoService; + + Self { + qido: None, + wado: Some(Box::new(S3WadoService::new(&config))), + stow: None, + } + } }; Ok(provider) diff --git a/src/config/mod.rs b/src/config/mod.rs index b84c61c..46e058e 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,6 +1,6 @@ use crate::types::AE; use crate::DEFAULT_AET; -use aws_credential_types::Credentials as AwsCredentials; + use serde::de::Error; use serde::{Deserialize, Deserializer}; use std::net::IpAddr; @@ -36,6 +36,7 @@ pub struct ApplicationEntityConfig { pub enum BackendConfig { #[serde(rename = "DIMSE")] Dimse(DimseConfig), + #[cfg(feature = "s3")] #[serde(rename = "S3")] S3(S3Config), } @@ -49,6 +50,7 @@ pub struct DimseConfig { pub pool: PoolConfig, } +#[cfg(feature = "s3")] #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "kebab-case")] pub struct S3Config { @@ -63,6 +65,7 @@ pub struct S3Config { pub endpoint_style: S3EndpointStyle, } +#[cfg(feature = "s3")] #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "lowercase")] pub enum S3EndpointStyle { @@ -70,12 +73,14 @@ pub enum S3EndpointStyle { VHost, } +#[cfg(feature = "s3")] impl Default for S3EndpointStyle { fn default() -> Self { Self::VHost } } +#[cfg(feature = "s3")] #[derive(Debug, Clone, Deserialize)] #[serde(untagged)] pub enum S3CredentialsConfig { @@ -91,13 +96,14 @@ pub enum S3CredentialsConfig { }, } +#[cfg(feature = "s3")] impl S3CredentialsConfig { - pub fn resolve(&self) -> Result { + pub fn resolve(&self) -> Result { match &self { Self::Plain { access_key, secret_key, - } => Ok(AwsCredentials::new( + } => Ok(aws_credential_types::Credentials::new( access_key, secret_key, None, @@ -110,7 +116,7 @@ impl S3CredentialsConfig { } => { let access_key = std::env::var(access_key_env)?; let secret_key = std::env::var(secret_key_env)?; - Ok(AwsCredentials::new( + Ok(aws_credential_types::Credentials::new( access_key, secret_key, None, @@ -274,14 +280,9 @@ pub enum Backend { } impl Default for Backend { - #[cfg(feature = "dimse")] fn default() -> Self { Self::Dimse } - #[cfg(not(feature = "dimse"))] - fn default() -> Self { - Self::Disabled - } } impl Default for DimseServerConfig { diff --git a/src/main.rs b/src/main.rs index 8766417..385898d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,9 +57,7 @@ fn init_logger(level: tracing::Level) { #[derive(Clone)] pub struct AppState { pub config: AppConfig, - #[cfg(feature = "dimse")] pub pools: AssociationPools, - #[cfg(feature = "dimse")] pub mediator: MoveMediator, } @@ -102,20 +100,15 @@ fn main() -> Result<(), Box> { } async fn run(config: AppConfig) -> anyhow::Result<()> { - #[cfg(feature = "dimse")] let mediator = MoveMediator::new(&config); - #[cfg(feature = "dimse")] let pools = AssociationPools::new(&config); let app_state = AppState { config: config.clone(), - #[cfg(feature = "dimse")] mediator: mediator.clone(), - #[cfg(feature = "dimse")] pools, }; - #[cfg(feature = "dimse")] for dimse_config in config.server.dimse { let mediator = mediator.clone(); let subscribers: Vec = config