perf: replace dyn trait objects with concrete types to eliminate vtable overhead
Remove async-trait dependency and use native Rust async fn in traits. Replace Arc<dyn Trait> with Arc<ConcreteType> throughout the codebase to enable monomorphization and eliminate dynamic dispatch overhead. Key changes: - Remove write-behind cache (no implementation existed) - Fix should_transcode static method call - Use ContactStorageAdapter directly instead of dyn AddressBookUseCase - Clean up unused trait imports across services and DI https://claude.ai/code/session_01EbAFEfyJNLRmJHmmYDX3Tt
This commit is contained in:
@@ -14,6 +14,7 @@ use crate::application::dtos::settings_dto::{
|
||||
use crate::common::di::AppState;
|
||||
use crate::interfaces::errors::AppError;
|
||||
use std::sync::Arc;
|
||||
use crate::application::ports::auth_ports::TokenServicePort;
|
||||
|
||||
/// Admin API routes — all require admin role.
|
||||
pub fn admin_routes() -> Router<Arc<AppState>> {
|
||||
|
||||
@@ -35,6 +35,7 @@ use crate::application::ports::calendar_ports::CalendarUseCase;
|
||||
use crate::common::di::AppState;
|
||||
use crate::interfaces::errors::AppError;
|
||||
use crate::interfaces::middleware::auth::CurrentUser;
|
||||
use crate::application::services::calendar_service::CalendarService;
|
||||
|
||||
const HEADER_DAV: HeaderName = HeaderName::from_static("dav");
|
||||
|
||||
@@ -127,7 +128,7 @@ fn extract_user(req: &Request<Body>) -> Result<CurrentUser, AppError> {
|
||||
.ok_or_else(|| AppError::unauthorized("Authentication required"))
|
||||
}
|
||||
|
||||
fn get_calendar_service(state: &AppState) -> Result<&Arc<dyn CalendarUseCase>, AppError> {
|
||||
fn get_calendar_service(state: &AppState) -> Result<&Arc<CalendarService>, AppError> {
|
||||
state.calendar_use_case.as_ref().ok_or_else(|| {
|
||||
AppError::new(
|
||||
StatusCode::NOT_IMPLEMENTED,
|
||||
|
||||
@@ -35,6 +35,7 @@ use crate::application::ports::carddav_ports::{AddressBookUseCase, ContactUseCas
|
||||
use crate::common::di::AppState;
|
||||
use crate::interfaces::errors::AppError;
|
||||
use crate::interfaces::middleware::auth::CurrentUser;
|
||||
use crate::infrastructure::adapters::contact_storage_adapter::ContactStorageAdapter;
|
||||
|
||||
const HEADER_DAV: HeaderName = HeaderName::from_static("dav");
|
||||
|
||||
@@ -115,7 +116,7 @@ fn extract_user(req: &Request<Body>) -> Result<CurrentUser, AppError> {
|
||||
.ok_or_else(|| AppError::unauthorized("Authentication required"))
|
||||
}
|
||||
|
||||
fn get_addressbook_service(state: &AppState) -> Result<&Arc<dyn AddressBookUseCase>, AppError> {
|
||||
fn get_addressbook_service(state: &AppState) -> Result<&Arc<ContactStorageAdapter>, AppError> {
|
||||
state.addressbook_use_case.as_ref().ok_or_else(|| {
|
||||
AppError::new(
|
||||
StatusCode::NOT_IMPLEMENTED,
|
||||
@@ -125,7 +126,7 @@ fn get_addressbook_service(state: &AppState) -> Result<&Arc<dyn AddressBookUseCa
|
||||
})
|
||||
}
|
||||
|
||||
fn get_contact_service(state: &AppState) -> Result<&Arc<dyn ContactUseCase>, AppError> {
|
||||
fn get_contact_service(state: &AppState) -> Result<&Arc<ContactStorageAdapter>, AppError> {
|
||||
state.contact_use_case.as_ref().ok_or_else(|| {
|
||||
AppError::new(
|
||||
StatusCode::NOT_IMPLEMENTED,
|
||||
|
||||
@@ -21,6 +21,9 @@ use crate::application::ports::chunked_upload_ports::DEFAULT_CHUNK_SIZE;
|
||||
use crate::common::di::AppState;
|
||||
use crate::domain::errors::ErrorKind;
|
||||
use crate::interfaces::middleware::auth::AuthUser;
|
||||
use crate::application::ports::chunked_upload_ports::ChunkedUploadPort;
|
||||
use crate::application::ports::file_ports::FileUploadUseCase;
|
||||
use crate::application::ports::storage_ports::StorageUsagePort;
|
||||
|
||||
/// Request body for creating an upload session
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
||||
@@ -10,6 +10,7 @@ use tracing::{error, info};
|
||||
|
||||
use crate::application::ports::favorites_ports::FavoritesUseCase;
|
||||
use crate::interfaces::middleware::auth::AuthUser;
|
||||
use crate::application::services::favorites_service::FavoritesService;
|
||||
|
||||
/// Single item in a batch-add-favorites request.
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -26,7 +27,7 @@ pub struct BatchFavoritesRequest {
|
||||
|
||||
/// Handler for favorite-related API endpoints
|
||||
pub async fn get_favorites(
|
||||
State(favorites_service): State<Arc<dyn FavoritesUseCase>>,
|
||||
State(favorites_service): State<Arc<FavoritesService>>,
|
||||
auth_user: AuthUser,
|
||||
) -> impl IntoResponse {
|
||||
let user_id = &auth_user.id;
|
||||
@@ -51,7 +52,7 @@ pub async fn get_favorites(
|
||||
|
||||
/// Add an item to user's favorites
|
||||
pub async fn add_favorite(
|
||||
State(favorites_service): State<Arc<dyn FavoritesUseCase>>,
|
||||
State(favorites_service): State<Arc<FavoritesService>>,
|
||||
auth_user: AuthUser,
|
||||
Path((item_type, item_id)): Path<(String, String)>,
|
||||
) -> impl IntoResponse {
|
||||
@@ -94,7 +95,7 @@ pub async fn add_favorite(
|
||||
|
||||
/// Remove an item from user's favorites
|
||||
pub async fn remove_favorite(
|
||||
State(favorites_service): State<Arc<dyn FavoritesUseCase>>,
|
||||
State(favorites_service): State<Arc<FavoritesService>>,
|
||||
auth_user: AuthUser,
|
||||
Path((item_type, item_id)): Path<(String, String)>,
|
||||
) -> impl IntoResponse {
|
||||
@@ -138,7 +139,7 @@ pub async fn remove_favorite(
|
||||
/// Add multiple items to favourites in a single transaction.
|
||||
/// POST /api/favorites/batch
|
||||
pub async fn batch_add_favorites(
|
||||
State(favorites_service): State<Arc<dyn FavoritesUseCase>>,
|
||||
State(favorites_service): State<Arc<FavoritesService>>,
|
||||
auth_user: AuthUser,
|
||||
Json(body): Json<BatchFavoritesRequest>,
|
||||
) -> impl IntoResponse {
|
||||
|
||||
@@ -14,6 +14,9 @@ use crate::application::ports::file_ports::OptimizedFileContent;
|
||||
use crate::common::di::AppState;
|
||||
use crate::interfaces::middleware::auth::{AuthUser, OptionalUserId};
|
||||
use std::sync::Arc;
|
||||
use crate::application::ports::file_ports::{FileManagementUseCase, FileRetrievalUseCase, FileUploadUseCase};
|
||||
use crate::application::ports::storage_ports::StorageUsagePort;
|
||||
use crate::application::ports::thumbnail_ports::ThumbnailPort;
|
||||
|
||||
/**
|
||||
* Type aliases for dependency injection state.
|
||||
@@ -306,7 +309,7 @@ impl FileHandler {
|
||||
let file_path = storage_root.join(&file.path);
|
||||
|
||||
match thumbnail_service
|
||||
.get_thumbnail(&id, thumb_size, &file_path)
|
||||
.get_thumbnail(&id, thumb_size.into(), &file_path)
|
||||
.await
|
||||
{
|
||||
Ok(data) => {
|
||||
|
||||
@@ -18,6 +18,8 @@ use crate::application::services::folder_service::FolderService;
|
||||
use crate::common::di::AppState as GlobalAppState;
|
||||
use crate::common::errors::ErrorKind;
|
||||
use crate::interfaces::middleware::auth::AuthUser;
|
||||
use crate::application::ports::file_ports::FileRetrievalUseCase;
|
||||
use crate::application::ports::trash_ports::TrashUseCase;
|
||||
|
||||
type AppState = Arc<FolderService>;
|
||||
|
||||
@@ -423,7 +425,17 @@ impl FolderHandler {
|
||||
tracing::info!("Preparing ZIP for folder: {} ({})", folder.name, id);
|
||||
|
||||
// Use ZIP service from DI container
|
||||
let zip_service = &state.core.zip_service;
|
||||
let zip_service = match &state.core.zip_service {
|
||||
Some(svc) => svc,
|
||||
None => {
|
||||
tracing::error!("ZipService not initialized");
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(serde_json::json!({ "error": "ZipService not initialized" })),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
};
|
||||
|
||||
// Create the ZIP archive (written to a temp file, O(1) RAM)
|
||||
match zip_service.create_folder_zip(&id, &folder.name).await {
|
||||
|
||||
@@ -10,6 +10,7 @@ use tracing::{error, info};
|
||||
|
||||
use crate::application::ports::recent_ports::RecentItemsUseCase;
|
||||
use crate::interfaces::middleware::auth::AuthUser;
|
||||
use crate::application::services::recent_service::RecentService;
|
||||
|
||||
/// Query parameters for getting recent items
|
||||
#[derive(Deserialize)]
|
||||
@@ -20,7 +21,7 @@ pub struct GetRecentParams {
|
||||
|
||||
/// Get user's recent items
|
||||
pub async fn get_recent_items(
|
||||
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
|
||||
State(recent_service): State<Arc<RecentService>>,
|
||||
auth_user: AuthUser,
|
||||
Query(params): Query<GetRecentParams>,
|
||||
) -> impl IntoResponse {
|
||||
@@ -46,7 +47,7 @@ pub async fn get_recent_items(
|
||||
|
||||
/// Record access to an item
|
||||
pub async fn record_item_access(
|
||||
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
|
||||
State(recent_service): State<Arc<RecentService>>,
|
||||
auth_user: AuthUser,
|
||||
Path((item_type, item_id)): Path<(String, String)>,
|
||||
) -> impl IntoResponse {
|
||||
@@ -92,7 +93,7 @@ pub async fn record_item_access(
|
||||
|
||||
/// Remove an item from recents
|
||||
pub async fn remove_from_recent(
|
||||
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
|
||||
State(recent_service): State<Arc<RecentService>>,
|
||||
auth_user: AuthUser,
|
||||
Path((item_type, item_id)): Path<(String, String)>,
|
||||
) -> impl IntoResponse {
|
||||
@@ -138,7 +139,7 @@ pub async fn remove_from_recent(
|
||||
|
||||
/// Clear all recent items
|
||||
pub async fn clear_recent_items(
|
||||
State(recent_service): State<Arc<dyn RecentItemsUseCase>>,
|
||||
State(recent_service): State<Arc<RecentService>>,
|
||||
auth_user: AuthUser,
|
||||
) -> impl IntoResponse {
|
||||
let user_id = &auth_user.id;
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::application::dtos::search_dto::SearchCriteriaDto;
|
||||
use crate::common::di::AppState;
|
||||
use crate::interfaces::middleware::auth::AuthUser;
|
||||
use std::sync::Arc;
|
||||
use crate::application::ports::inbound::SearchUseCase;
|
||||
|
||||
/**
|
||||
* Handler for search operations through the API.
|
||||
|
||||
@@ -18,6 +18,7 @@ use crate::{
|
||||
domain::entities::share::ShareItemType,
|
||||
interfaces::middleware::auth::OptionalAuthUser,
|
||||
};
|
||||
use crate::application::services::share_service::ShareService;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct GetSharesQuery {
|
||||
@@ -34,7 +35,7 @@ pub struct VerifyPasswordRequest {
|
||||
|
||||
/// Create a new shared link
|
||||
pub async fn create_shared_link(
|
||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
||||
State(share_use_case): State<Arc<ShareService>>,
|
||||
auth_user: OptionalAuthUser,
|
||||
Json(dto): Json<CreateShareDto>,
|
||||
) -> impl IntoResponse {
|
||||
@@ -57,7 +58,7 @@ pub async fn create_shared_link(
|
||||
|
||||
/// Get information about a specific shared link by ID
|
||||
pub async fn get_shared_link(
|
||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
||||
State(share_use_case): State<Arc<ShareService>>,
|
||||
Path(id): Path<String>,
|
||||
) -> impl IntoResponse {
|
||||
match share_use_case.get_shared_link(&id).await {
|
||||
@@ -75,7 +76,7 @@ pub async fn get_shared_link(
|
||||
/// Get all shared links created by the current user.
|
||||
/// Supports optional filtering by item_id + item_type query params.
|
||||
pub async fn get_user_shares(
|
||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
||||
State(share_use_case): State<Arc<ShareService>>,
|
||||
auth_user: OptionalAuthUser,
|
||||
Query(query): Query<GetSharesQuery>,
|
||||
) -> impl IntoResponse {
|
||||
@@ -128,7 +129,7 @@ pub async fn get_user_shares(
|
||||
|
||||
/// Update a shared link's properties
|
||||
pub async fn update_shared_link(
|
||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
||||
State(share_use_case): State<Arc<ShareService>>,
|
||||
Path(id): Path<String>,
|
||||
Json(dto): Json<UpdateShareDto>,
|
||||
) -> impl IntoResponse {
|
||||
@@ -148,7 +149,7 @@ pub async fn update_shared_link(
|
||||
|
||||
/// Delete a shared link
|
||||
pub async fn delete_shared_link(
|
||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
||||
State(share_use_case): State<Arc<ShareService>>,
|
||||
Path(id): Path<String>,
|
||||
) -> impl IntoResponse {
|
||||
match share_use_case.delete_shared_link(&id).await {
|
||||
@@ -166,7 +167,7 @@ pub async fn delete_shared_link(
|
||||
|
||||
/// Access a shared item via its token
|
||||
pub async fn access_shared_item(
|
||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
||||
State(share_use_case): State<Arc<ShareService>>,
|
||||
Path(token): Path<String>,
|
||||
) -> impl IntoResponse {
|
||||
// Register the access
|
||||
@@ -204,7 +205,7 @@ pub async fn access_shared_item(
|
||||
|
||||
/// Verify password for a password-protected shared item
|
||||
pub async fn verify_shared_item_password(
|
||||
State(share_use_case): State<Arc<dyn ShareUseCase>>,
|
||||
State(share_use_case): State<Arc<ShareService>>,
|
||||
Path(token): Path<String>,
|
||||
Json(req): Json<VerifyPasswordRequest>,
|
||||
) -> impl IntoResponse {
|
||||
|
||||
@@ -4,7 +4,7 @@ use axum::http::StatusCode;
|
||||
use serde_json::json;
|
||||
use tracing::{debug, error, instrument, warn};
|
||||
|
||||
// use crate::application::ports::trash_ports::TrashUseCase;
|
||||
use crate::application::ports::trash_ports::TrashUseCase;
|
||||
use crate::common::di::AppState;
|
||||
use crate::interfaces::middleware::auth::{AuthUser, OptionalAuthUser};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -28,8 +28,11 @@ use crate::common::di::AppState;
|
||||
use crate::infrastructure::services::path_resolver_service::ResolvedResource;
|
||||
use crate::interfaces::errors::AppError;
|
||||
use crate::interfaces::middleware::auth::CurrentUser;
|
||||
use crate::application::services::file_retrieval_service::FileRetrievalService;
|
||||
use crate::application::services::folder_service::FolderService;
|
||||
use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, percent_decode_str, utf8_percent_encode};
|
||||
use std::sync::Arc;
|
||||
use crate::application::ports::file_ports::{FileManagementUseCase, FileUploadUseCase};
|
||||
|
||||
/// Characters that MUST NOT be percent-encoded inside a URI path segment.
|
||||
/// RFC 3986 §3.3 pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
@@ -394,8 +397,8 @@ async fn build_streaming_propfind_response(
|
||||
depth: &str,
|
||||
base_href: &str,
|
||||
propfind_request: PropFindRequest,
|
||||
folder_service: std::sync::Arc<dyn FolderUseCase>,
|
||||
file_retrieval_service: std::sync::Arc<dyn FileRetrievalUseCase>,
|
||||
folder_service: std::sync::Arc<FolderService>,
|
||||
file_retrieval_service: std::sync::Arc<FileRetrievalService>,
|
||||
) -> Result<Response<Body>, AppError> {
|
||||
let depth = depth.to_string();
|
||||
let base_href = base_href.to_string();
|
||||
|
||||
@@ -23,6 +23,7 @@ use std::sync::Arc;
|
||||
use crate::application::services::wopi_lock_service::WopiLockService;
|
||||
use crate::application::services::wopi_token_service::WopiTokenService;
|
||||
use crate::infrastructure::services::wopi_discovery_service::WopiDiscoveryService;
|
||||
use crate::application::ports::file_ports::{FileRetrievalUseCase, FileUploadUseCase};
|
||||
|
||||
/// Shared state for WOPI handlers.
|
||||
#[derive(Clone)]
|
||||
|
||||
@@ -8,6 +8,7 @@ use axum::{
|
||||
use serde_json::json;
|
||||
use std::sync::Arc;
|
||||
use tower_http::{compression::CompressionLayer, trace::TraceLayer};
|
||||
use crate::application::services::batch_operations::BatchOperationService;
|
||||
|
||||
/// Returns the application version from Cargo.toml (compile-time constant)
|
||||
async fn get_version() -> AxumJson<serde_json::Value> {
|
||||
@@ -17,7 +18,6 @@ async fn get_version() -> AxumJson<serde_json::Value> {
|
||||
}))
|
||||
}
|
||||
|
||||
use crate::application::services::batch_operations::BatchOperationService;
|
||||
|
||||
use crate::interfaces::api::handlers::admin_handler;
|
||||
use crate::interfaces::api::handlers::batch_handler::{self, BatchHandlerState};
|
||||
|
||||
Reference in New Issue
Block a user