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:
Claude
2026-03-03 15:36:42 +00:00
parent efcf88c4d7
commit 1b49135ca9
101 changed files with 346 additions and 576 deletions
@@ -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 {
+4 -1
View File
@@ -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) => {
+13 -1
View File
@@ -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.
+8 -7
View File
@@ -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 {
+1 -1
View File
@@ -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)]