diff --git a/src/application/services/batch_operations.rs b/src/application/services/batch_operations.rs index ad4555a0..d235f0b6 100644 --- a/src/application/services/batch_operations.rs +++ b/src/application/services/batch_operations.rs @@ -733,18 +733,16 @@ impl BatchOperationService { } // ── Finalize ───────────────────────────────────────────────────── - let mut compat_writer = zip.close().await.map_err(|e| { - BatchOperationError::Internal(format!("ZIP finalize error: {}", e)) - })?; - compat_writer.close().await.map_err(|e| { - BatchOperationError::Internal(format!("ZIP flush error: {}", e)) - })?; + let mut compat_writer = zip + .close() + .await + .map_err(|e| BatchOperationError::Internal(format!("ZIP finalize error: {}", e)))?; + compat_writer + .close() + .await + .map_err(|e| BatchOperationError::Internal(format!("ZIP flush error: {}", e)))?; - let file_size = temp - .as_file() - .metadata() - .map(|m| m.len()) - .unwrap_or(0); + let file_size = temp.as_file().metadata().map(|m| m.len()).unwrap_or(0); info!( "Batch download ZIP created: {} bytes in {}ms", @@ -776,17 +774,18 @@ impl BatchOperationService { let mut stream = std::pin::Pin::from(stream); while let Some(chunk) = stream.next().await { - let bytes = chunk.map_err(|e| { - BatchOperationError::Internal(format!("stream read: {}", e)) - })?; - writer.write_all(&bytes).await.map_err(|e| { - BatchOperationError::Internal(format!("zip chunk write: {}", e)) - })?; + let bytes = + chunk.map_err(|e| BatchOperationError::Internal(format!("stream read: {}", e)))?; + writer + .write_all(&bytes) + .await + .map_err(|e| BatchOperationError::Internal(format!("zip chunk write: {}", e)))?; } - writer.close().await.map_err(|e| { - BatchOperationError::Internal(format!("zip entry close: {}", e)) - })?; + writer + .close() + .await + .map_err(|e| BatchOperationError::Internal(format!("zip entry close: {}", e)))?; Ok(()) } diff --git a/src/application/services/file_upload_service.rs b/src/application/services/file_upload_service.rs index ce8e5e42..21cc5471 100644 --- a/src/application/services/file_upload_service.rs +++ b/src/application/services/file_upload_service.rs @@ -194,12 +194,11 @@ impl FileUploadUseCase for FileUploadService { }; // Spool to temp file + hash - let temp = tempfile::NamedTempFile::new().map_err(|e| { - DomainError::internal_error("FileUpload", format!("temp file: {e}")) - })?; - tokio::fs::write(temp.path(), content).await.map_err(|e| { - DomainError::internal_error("FileUpload", format!("write temp: {e}")) - })?; + let temp = tempfile::NamedTempFile::new() + .map_err(|e| DomainError::internal_error("FileUpload", format!("temp file: {e}")))?; + tokio::fs::write(temp.path(), content) + .await + .map_err(|e| DomainError::internal_error("FileUpload", format!("write temp: {e}")))?; let hash = hex::encode(Sha256::digest(content)); let file = self @@ -224,12 +223,11 @@ impl FileUploadUseCase for FileUploadService { /// then delegates to the streaming update/create path. async fn update_file(&self, path: &str, content: &[u8]) -> Result<(), DomainError> { // Spool to temp file + hash - let temp = tempfile::NamedTempFile::new().map_err(|e| { - DomainError::internal_error("FileUpload", format!("temp file: {e}")) - })?; - tokio::fs::write(temp.path(), content).await.map_err(|e| { - DomainError::internal_error("FileUpload", format!("write temp: {e}")) - })?; + let temp = tempfile::NamedTempFile::new() + .map_err(|e| DomainError::internal_error("FileUpload", format!("temp file: {e}")))?; + tokio::fs::write(temp.path(), content) + .await + .map_err(|e| DomainError::internal_error("FileUpload", format!("write temp: {e}")))?; let hash = hex::encode(Sha256::digest(content)); self.update_file_streaming( diff --git a/src/application/services/search_service.rs b/src/application/services/search_service.rs index 84675248..d77ec418 100644 --- a/src/application/services/search_service.rs +++ b/src/application/services/search_service.rs @@ -272,7 +272,11 @@ impl SearchUseCase for SearchService { * - Human-readable size formatting * - Pagination */ - async fn search(&self, criteria: SearchCriteriaDto, user_id: &str) -> Result> { + async fn search( + &self, + criteria: SearchCriteriaDto, + user_id: &str, + ) -> Result> { let start = Instant::now(); // Try to get from cache @@ -369,7 +373,8 @@ impl SearchUseCase for SearchService { criteria.sort_by.clone(), )); - self.store_in_cache(cache_key, Arc::clone(&search_results)).await; + self.store_in_cache(cache_key, Arc::clone(&search_results)) + .await; return Ok(search_results); } @@ -454,7 +459,8 @@ impl SearchUseCase for SearchService { )); // Store in cache — Arc::clone is ~1 ns (atomic increment) - self.store_in_cache(cache_key, Arc::clone(&search_results)).await; + self.store_in_cache(cache_key, Arc::clone(&search_results)) + .await; Ok(search_results) } diff --git a/src/application/services/share_service.rs b/src/application/services/share_service.rs index 555c824c..d0ce5aa4 100644 --- a/src/application/services/share_service.rs +++ b/src/application/services/share_service.rs @@ -510,7 +510,13 @@ mod tests { &self, _folder_id: &str, ) -> Result< - std::pin::Pin> + Send>>, + std::pin::Pin< + Box< + dyn futures::Stream< + Item = Result, + > + Send, + >, + >, DomainError, > { Ok(Box::pin(futures::stream::empty())) diff --git a/src/application/services/trash_service_test.rs b/src/application/services/trash_service_test.rs index 02aaa37f..890e31af 100644 --- a/src/application/services/trash_service_test.rs +++ b/src/application/services/trash_service_test.rs @@ -208,7 +208,10 @@ impl FileReadPort for MockFileRepository { async fn stream_files_in_subtree( &self, _folder_id: &str, - ) -> std::result::Result> + Send>>, DomainError> { + ) -> std::result::Result< + Pin> + Send>>, + DomainError, + > { Ok(Box::pin(futures::stream::empty())) } } @@ -502,7 +505,10 @@ mod tests { // Arrange let trashed_files = Arc::new(Mutex::new(HashMap::new())); let trashed_folders = Arc::new(Mutex::new(HashMap::new())); - let trash_repo = Arc::new(MockTrashRepository::new(trashed_files.clone(), trashed_folders.clone())); + let trash_repo = Arc::new(MockTrashRepository::new( + trashed_files.clone(), + trashed_folders.clone(), + )); let file_repo = Arc::new(MockFileRepository::new(trashed_files)); let folder_repo = Arc::new(MockFolderRepository::new(trashed_folders)); @@ -573,7 +579,10 @@ mod tests { // Arrange let trashed_files = Arc::new(Mutex::new(HashMap::new())); let trashed_folders = Arc::new(Mutex::new(HashMap::new())); - let trash_repo = Arc::new(MockTrashRepository::new(trashed_files.clone(), trashed_folders.clone())); + let trash_repo = Arc::new(MockTrashRepository::new( + trashed_files.clone(), + trashed_folders.clone(), + )); let file_repo = Arc::new(MockFileRepository::new(trashed_files)); let folder_repo = Arc::new(MockFolderRepository::new(trashed_folders)); @@ -635,7 +644,10 @@ mod tests { // Arrange let trashed_files = Arc::new(Mutex::new(HashMap::new())); let trashed_folders = Arc::new(Mutex::new(HashMap::new())); - let trash_repo = Arc::new(MockTrashRepository::new(trashed_files.clone(), trashed_folders.clone())); + let trash_repo = Arc::new(MockTrashRepository::new( + trashed_files.clone(), + trashed_folders.clone(), + )); let file_repo = Arc::new(MockFileRepository::new(trashed_files)); let folder_repo = Arc::new(MockFolderRepository::new(trashed_folders)); @@ -702,7 +714,10 @@ mod tests { // Arrange let trashed_files = Arc::new(Mutex::new(HashMap::new())); let trashed_folders = Arc::new(Mutex::new(HashMap::new())); - let trash_repo = Arc::new(MockTrashRepository::new(trashed_files.clone(), trashed_folders.clone())); + let trash_repo = Arc::new(MockTrashRepository::new( + trashed_files.clone(), + trashed_folders.clone(), + )); let file_repo = Arc::new(MockFileRepository::new(trashed_files)); let folder_repo = Arc::new(MockFolderRepository::new(trashed_folders)); @@ -768,7 +783,10 @@ mod tests { // Arrange let trashed_files = Arc::new(Mutex::new(HashMap::new())); let trashed_folders = Arc::new(Mutex::new(HashMap::new())); - let trash_repo = Arc::new(MockTrashRepository::new(trashed_files.clone(), trashed_folders.clone())); + let trash_repo = Arc::new(MockTrashRepository::new( + trashed_files.clone(), + trashed_folders.clone(), + )); let file_repo = Arc::new(MockFileRepository::new(trashed_files)); let folder_repo = Arc::new(MockFolderRepository::new(trashed_folders)); diff --git a/src/common/di.rs b/src/common/di.rs index 3d20b256..9d3974d5 100644 --- a/src/common/di.rs +++ b/src/common/di.rs @@ -36,10 +36,10 @@ use crate::application::services::{ use crate::common::config::AppConfig; use crate::common::errors::DomainError; use crate::domain::services::i18n_service::I18nService; +use crate::infrastructure::repositories::pg::SharePgRepository; use crate::infrastructure::repositories::pg::{ FileBlobReadRepository, FileBlobWriteRepository, FolderDbRepository, TrashDbRepository, }; -use crate::infrastructure::repositories::pg::SharePgRepository; use crate::infrastructure::services::file_content_cache::{ FileContentCache, FileContentCacheConfig, }; @@ -333,11 +333,13 @@ impl AppServiceFactory { // Build a password hasher for share password verification let password_hasher: Arc = - Arc::new(crate::infrastructure::services::password_hasher::Argon2PasswordHasher::new( - self.config.auth.hash_memory_cost, - self.config.auth.hash_time_cost, - self.config.auth.hash_parallelism, - )); + Arc::new( + crate::infrastructure::services::password_hasher::Argon2PasswordHasher::new( + self.config.auth.hash_memory_cost, + self.config.auth.hash_time_cost, + self.config.auth.hash_parallelism, + ), + ); let service = Arc::new(ShareService::new( Arc::new(self.config.clone()), @@ -469,7 +471,8 @@ impl AppServiceFactory { recent_service = Some(recent.clone()); apps.recent_service = Some(recent); - storage_usage_service = Some(self.create_storage_usage_service(&repos, &pool, &maintenance_pool)); + storage_usage_service = + Some(self.create_storage_usage_service(&repos, &pool, &maintenance_pool)); // Auth services if self.config.features.enable_auth { diff --git a/src/infrastructure/repositories/pg/trash_db_repository.rs b/src/infrastructure/repositories/pg/trash_db_repository.rs index 5c7655f4..6ce2c41a 100644 --- a/src/infrastructure/repositories/pg/trash_db_repository.rs +++ b/src/infrastructure/repositories/pg/trash_db_repository.rs @@ -150,38 +150,40 @@ impl TrashRepository for TrashDbRepository { async fn delete_expired_bulk(&self) -> Result<(u64, u64)> { let cutoff = Utc::now() - chrono::Duration::days(self.retention_days); - let mut tx = self.pool.begin().await.map_err(|e| { - DomainError::internal_error("TrashDb", format!("begin tx: {e}")) - })?; + let mut tx = self + .pool + .begin() + .await + .map_err(|e| DomainError::internal_error("TrashDb", format!("begin tx: {e}")))?; // 1. Bulk-delete expired trashed files. // The PG trigger `trg_files_decrement_blob_ref` automatically // decrements blob ref_count for every deleted row. - let files_deleted = sqlx::query( - "DELETE FROM storage.files WHERE is_trashed = TRUE AND trashed_at < $1", - ) - .bind(cutoff) - .execute(&mut *tx) - .await - .map_err(|e| DomainError::internal_error("TrashDb", format!("bulk delete files: {e}")))? - .rows_affected(); + let files_deleted = + sqlx::query("DELETE FROM storage.files WHERE is_trashed = TRUE AND trashed_at < $1") + .bind(cutoff) + .execute(&mut *tx) + .await + .map_err(|e| { + DomainError::internal_error("TrashDb", format!("bulk delete files: {e}")) + })? + .rows_affected(); // 2. Bulk-delete expired trashed folders. // FK ON DELETE CASCADE handles descendant folders and their files. - let folders_deleted = sqlx::query( - "DELETE FROM storage.folders WHERE is_trashed = TRUE AND trashed_at < $1", - ) - .bind(cutoff) - .execute(&mut *tx) - .await - .map_err(|e| { - DomainError::internal_error("TrashDb", format!("bulk delete folders: {e}")) - })? - .rows_affected(); + let folders_deleted = + sqlx::query("DELETE FROM storage.folders WHERE is_trashed = TRUE AND trashed_at < $1") + .bind(cutoff) + .execute(&mut *tx) + .await + .map_err(|e| { + DomainError::internal_error("TrashDb", format!("bulk delete folders: {e}")) + })? + .rows_affected(); - tx.commit().await.map_err(|e| { - DomainError::internal_error("TrashDb", format!("commit tx: {e}")) - })?; + tx.commit() + .await + .map_err(|e| DomainError::internal_error("TrashDb", format!("commit tx: {e}")))?; Ok((files_deleted, folders_deleted)) } diff --git a/src/infrastructure/services/chunked_upload_service.rs b/src/infrastructure/services/chunked_upload_service.rs index 288fa3c7..7e61fc75 100644 --- a/src/infrastructure/services/chunked_upload_service.rs +++ b/src/infrastructure/services/chunked_upload_service.rs @@ -157,8 +157,8 @@ impl UploadSession { /// Persist the full session metadata once (on create). async fn persist_metadata(&self) -> Result<(), String> { let path = self.temp_dir.join(SESSION_META_FILE); - let json = serde_json::to_vec(self) - .map_err(|e| format!("Failed to serialise session: {e}"))?; + let json = + serde_json::to_vec(self).map_err(|e| format!("Failed to serialise session: {e}"))?; // Atomic write: write to .tmp then rename let tmp = self.temp_dir.join("session.json.tmp"); fs::write(&tmp, &json) @@ -213,9 +213,7 @@ impl ChunkedUploadService { }; if recovered_count > 0 { - tracing::info!( - "♻️ Recovered {recovered_count} chunked-upload session(s) from disk" - ); + tracing::info!("♻️ Recovered {recovered_count} chunked-upload session(s) from disk"); } // Start cleanup task @@ -325,10 +323,7 @@ impl ChunkedUploadService { // ── Cleanup ────────────────────────────────────────────────────────── /// Background task to clean expired sessions - async fn cleanup_loop( - sessions: Arc>, - temp_base_dir: PathBuf, - ) { + async fn cleanup_loop(sessions: Arc>, temp_base_dir: PathBuf) { let mut interval = tokio::time::interval(Duration::from_secs(3600)); // Every hour loop { @@ -463,7 +458,8 @@ impl ChunkedUploadService { ) -> Result { // Validate session exists and chunk index is valid let (chunk_path, expected_size) = { - let session = self.sessions + let session = self + .sessions .get(upload_id) .ok_or_else(|| format!("Upload session not found: {}", upload_id))?; @@ -500,11 +496,10 @@ impl ChunkedUploadService { // worker free for other connections. if let Some(ref expected_checksum) = checksum { let data_clone = data.clone(); // Bytes::clone is O(1) — just an Arc increment - let actual_checksum = tokio::task::spawn_blocking(move || { - format!("{:x}", md5::compute(&data_clone)) - }) - .await - .map_err(|e| format!("MD5 checksum task failed: {e}"))?; + let actual_checksum = + tokio::task::spawn_blocking(move || format!("{:x}", md5::compute(&data_clone))) + .await + .map_err(|e| format!("MD5 checksum task failed: {e}"))?; if actual_checksum != *expected_checksum { return Err(format!( @@ -527,7 +522,8 @@ impl ChunkedUploadService { // Disk I/O (persist_progress) is done AFTER the ref is dropped so // concurrent uploads to other sessions are never blocked. let (bytes_received, progress, is_complete, persist_path, persist_bitmask) = { - let mut session = self.sessions + let mut session = self + .sessions .get_mut(upload_id) .ok_or_else(|| "Session disappeared".to_string())?; @@ -571,11 +567,9 @@ impl ChunkedUploadService { } /// Get upload status - async fn get_status_inner( - &self, - upload_id: &str, - ) -> Result { - let session = self.sessions + async fn get_status_inner(&self, upload_id: &str) -> Result { + let session = self + .sessions .get(upload_id) .ok_or_else(|| format!("Upload session not found: {}", upload_id))?; @@ -613,7 +607,8 @@ impl ChunkedUploadService { // Clone the session data and drop the DashMap ref immediately // so the shard is not held during the expensive assembly step. let session = { - let entry = self.sessions + let entry = self + .sessions .get(upload_id) .ok_or_else(|| format!("Upload session not found: {}", upload_id))?; @@ -640,12 +635,17 @@ impl ChunkedUploadService { let chunks_meta: Vec<(usize, PathBuf)> = session .chunks .iter() - .map(|c| (c.index, session.temp_dir.join(format!("chunk_{:06}", c.index)))) + .map(|c| { + ( + c.index, + session.temp_dir.join(format!("chunk_{:06}", c.index)), + ) + }) .collect(); let total_size = session.total_size; let hash = tokio::task::spawn_blocking(move || -> Result { - use std::io::{Read, Write, BufWriter as StdBufWriter}; + use std::io::{BufWriter as StdBufWriter, Read, Write}; let raw_output = std::fs::OpenOptions::new() .create(true) @@ -920,8 +920,7 @@ mod tests { }; let json = serde_json::to_vec(&session).expect("serialise"); - let restored: UploadSession = - serde_json::from_slice(&json).expect("deserialise"); + let restored: UploadSession = serde_json::from_slice(&json).expect("deserialise"); assert_eq!(restored.id, session.id); assert_eq!(restored.filename, session.filename); @@ -996,7 +995,9 @@ mod tests { let recovered = ChunkedUploadService::recover_sessions(&base).await; assert_eq!(recovered.len(), 1); - let session = recovered.get(&upload_id).expect("session must be recovered"); + let session = recovered + .get(&upload_id) + .expect("session must be recovered"); assert_eq!(session.filename, "bigfile.bin"); assert_eq!(session.folder_id, Some("folder-x".into())); assert_eq!(session.chunks[0].status, ChunkStatus::Complete); @@ -1051,10 +1052,8 @@ mod tests { assert!(status.pending_chunks.is_empty()); // 4. Complete (assemble) - let (path, filename, _folder, _ct, size, hash) = service - .complete_upload_inner(&id) - .await - .expect("complete"); + let (path, filename, _folder, _ct, size, hash) = + service.complete_upload_inner(&id).await.expect("complete"); assert_eq!(filename, "test.txt"); assert_eq!(size, 1024); assert!(!hash.is_empty()); @@ -1078,7 +1077,13 @@ mod tests { let service = ChunkedUploadService::new(base.clone()).await; let resp = service - .create_session_inner("x.bin".into(), None, "application/octet-stream".into(), 512, Some(512)) + .create_session_inner( + "x.bin".into(), + None, + "application/octet-stream".into(), + 512, + Some(512), + ) .await .expect("create"); @@ -1156,12 +1161,18 @@ mod tests { chunk_size: 512, chunks: vec![ ChunkInfo { - index: 0, offset: 0, size: 512, - status: ChunkStatus::Pending, checksum: None, + index: 0, + offset: 0, + size: 512, + status: ChunkStatus::Pending, + checksum: None, }, ChunkInfo { - index: 1, offset: 512, size: 512, - status: ChunkStatus::Pending, checksum: None, + index: 1, + offset: 512, + size: 512, + status: ChunkStatus::Pending, + checksum: None, }, ], created_at: Utc::now(), @@ -1172,14 +1183,20 @@ mod tests { // Write metadata let json = serde_json::to_vec(&session).unwrap(); - fs::write(session_dir.join(SESSION_META_FILE), &json).await.unwrap(); + fs::write(session_dir.join(SESSION_META_FILE), &json) + .await + .unwrap(); // Write progress marking both chunks complete let bitmask = vec![0b00000011u8]; // bits 0 and 1 - fs::write(session_dir.join(PROGRESS_FILE), &bitmask).await.unwrap(); + fs::write(session_dir.join(PROGRESS_FILE), &bitmask) + .await + .unwrap(); // But only create chunk_000000 on disk — chunk_000001 is "missing" - fs::write(session_dir.join("chunk_000000"), &[0u8; 512]).await.unwrap(); + fs::write(session_dir.join("chunk_000000"), &[0u8; 512]) + .await + .unwrap(); let recovered = ChunkedUploadService::recover_sessions(&base).await; let s = recovered.get("partial-session").expect("must be recovered"); diff --git a/src/infrastructure/services/dedup_service.rs b/src/infrastructure/services/dedup_service.rs index 757b6443..773aa7a5 100644 --- a/src/infrastructure/services/dedup_service.rs +++ b/src/infrastructure/services/dedup_service.rs @@ -769,9 +769,7 @@ impl DedupService { .bind(BATCH_SIZE) .fetch_all(self.maintenance_pool.as_ref()) .await - .map_err(|e| { - DomainError::internal_error("Dedup", format!("GC batch failed: {e}")) - })?; + .map_err(|e| DomainError::internal_error("Dedup", format!("GC batch failed: {e}")))?; if batch.is_empty() { break; diff --git a/src/infrastructure/services/thumbnail_service.rs b/src/infrastructure/services/thumbnail_service.rs index 4532ebe4..130bb73c 100644 --- a/src/infrastructure/services/thumbnail_service.rs +++ b/src/infrastructure/services/thumbnail_service.rs @@ -192,11 +192,7 @@ impl ThumbnailService { } // 2. Generate thumbnail (CPU-bound, runs in spawn_blocking) - tracing::info!( - "🎨 Generating thumbnail: {} {:?}", - file_id_owned, - size - ); + tracing::info!("🎨 Generating thumbnail: {} {:?}", file_id_owned, size); match self.generate_thumbnail(&original_owned, size).await { Ok(bytes) => { // Save to disk (best-effort — don't fail the request) @@ -245,14 +241,17 @@ impl ThumbnailService { let max_dim = size.max_dimension(); // Acquire semaphore permit — bounds peak RAM from concurrent decodes - let _permit = self.decode_semaphore.acquire().await + let _permit = self + .decode_semaphore + .acquire() + .await .map_err(|_| ThumbnailError::TaskError("Decode semaphore closed".into()))?; // Run image processing in blocking thread pool let result = tokio::task::spawn_blocking(move || -> Result, ThumbnailError> { // Single read: load file once into memory, then work from the buffer - let data = std::fs::read(&path) - .map_err(|e| ThumbnailError::ImageError(e.to_string()))?; + let data = + std::fs::read(&path).map_err(|e| ThumbnailError::ImageError(e.to_string()))?; // Safety check: read dimensions from in-memory buffer (no 2nd I/O) let (w, h) = image::ImageReader::new(std::io::Cursor::new(&data)) @@ -284,9 +283,9 @@ impl ThumbnailService { // Adaptive filter: faster filters for smaller sizes where // quality difference vs Lanczos3 is imperceptible let filter = match size { - ThumbnailSize::Icon => FilterType::Triangle, // 150px — max speed + ThumbnailSize::Icon => FilterType::Triangle, // 150px — max speed ThumbnailSize::Preview => FilterType::CatmullRom, // 400px — good balance - ThumbnailSize::Large => FilterType::CatmullRom, // 800px — sufficient quality + ThumbnailSize::Large => FilterType::CatmullRom, // 800px — sufficient quality }; let thumbnail = img.resize(new_width, new_height, filter); @@ -318,7 +317,10 @@ impl ThumbnailService { let _permit = match self.decode_semaphore.acquire().await { Ok(p) => p, Err(_) => { - tracing::warn!("Decode semaphore closed, skipping thumbnails for {}", file_id); + tracing::warn!( + "Decode semaphore closed, skipping thumbnails for {}", + file_id + ); return; } }; @@ -328,8 +330,8 @@ impl ThumbnailService { // Single spawn_blocking: 1 read + 1 decode + 3 resize + 3 encode let results = tokio::task::spawn_blocking(move || { // Single read: load file once into memory - let data = std::fs::read(&path) - .map_err(|e| ThumbnailError::ImageError(e.to_string()))?; + let data = + std::fs::read(&path).map_err(|e| ThumbnailError::ImageError(e.to_string()))?; // Safety check: read dimensions from in-memory buffer (no 2nd I/O) let (w, h) = image::ImageReader::new(std::io::Cursor::new(&data)) @@ -364,18 +366,15 @@ impl ThumbnailService { }; let filter = match size { - ThumbnailSize::Icon => FilterType::Triangle, + ThumbnailSize::Icon => FilterType::Triangle, ThumbnailSize::Preview => FilterType::CatmullRom, - ThumbnailSize::Large => FilterType::CatmullRom, + ThumbnailSize::Large => FilterType::CatmullRom, }; let thumb = img.resize(new_w, new_h, filter); let mut buf = Vec::new(); thumb - .write_to( - &mut std::io::Cursor::new(&mut buf), - ImageFormat::WebP, - ) + .write_to(&mut std::io::Cursor::new(&mut buf), ImageFormat::WebP) .map_err(|e| ThumbnailError::ImageError(e.to_string()))?; Ok((size, Bytes::from(buf))) @@ -388,15 +387,11 @@ impl ThumbnailService { let thumbnails = match results { Ok(Ok(t)) => t, Ok(Err(e)) => { - tracing::warn!( - "Thumbnail generation failed for {}: {}", file_id, e - ); + tracing::warn!("Thumbnail generation failed for {}: {}", file_id, e); return; } Err(e) => { - tracing::warn!( - "Thumbnail task panicked for {}: {}", file_id, e - ); + tracing::warn!("Thumbnail task panicked for {}: {}", file_id, e); return; } }; diff --git a/src/infrastructure/services/trash_cleanup_service.rs b/src/infrastructure/services/trash_cleanup_service.rs index 5fbcbe1a..66ced330 100644 --- a/src/infrastructure/services/trash_cleanup_service.rs +++ b/src/infrastructure/services/trash_cleanup_service.rs @@ -17,10 +17,7 @@ pub struct TrashCleanupService { } impl TrashCleanupService { - pub fn new( - trash_repository: Arc, - cleanup_interval_hours: u64, - ) -> Self { + pub fn new(trash_repository: Arc, cleanup_interval_hours: u64) -> Self { Self { trash_repository, cleanup_interval_hours: cleanup_interval_hours.max(1), // Minimum 1 hour @@ -60,9 +57,7 @@ impl TrashCleanupService { /// Bulk-delete all expired trash items in a single transaction. #[instrument(skip(trash_repository))] - async fn cleanup_expired_items( - trash_repository: Arc, - ) -> Result<()> { + async fn cleanup_expired_items(trash_repository: Arc) -> Result<()> { debug!("Starting bulk cleanup of expired trash items"); let (files, folders) = trash_repository.delete_expired_bulk().await?; diff --git a/src/interfaces/api/handlers/batch_handler.rs b/src/interfaces/api/handlers/batch_handler.rs index 5f848819..2a0c3da6 100644 --- a/src/interfaces/api/handlers/batch_handler.rs +++ b/src/interfaces/api/handlers/batch_handler.rs @@ -616,10 +616,12 @@ pub async fn download_batch( .as_file() .metadata() .map(|m| m.len()) - .map_err(|e| ( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Failed to read temp file metadata: {}", e), - ))?; + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Failed to read temp file metadata: {}", e), + ) + })?; // Split into the already-open fd + auto-delete path let (std_file, temp_path) = temp_file.into_parts(); @@ -644,7 +646,9 @@ pub async fn download_batch( // Keep TempPath alive in response extensions so the file is only // deleted AFTER the body stream finishes sending. - response.extensions_mut().insert(std::sync::Arc::new(temp_path)); + response + .extensions_mut() + .insert(std::sync::Arc::new(temp_path)); Ok(response) }