fix: resolve all clippy warnings and enforce cargo fmt
- display_helpers: convert module doc-comments to regular comments, merge identical text/markdown + text/ branches - search_service: replace needless range loops with slice-based pagination - folder_repository, folder_db_repository: collapse nested if statements - favorites_pg_repository: remove unnecessary borrow on generic arg - file_blob_read_repository: collapse 6 nested if-let blocks - file_blob_write_repository: collapse nested if for dedup ref decrement - chunked_upload_service: use div_ceil(), collapse 2 nested if blocks - folder_handler: collapse nested if-let for owner check - webdav_handler: replace 7x io::Error::new(ErrorKind::Other, ..) with io::Error::other(..) - cargo fmt applied to all files Passes: cargo clippy --all-targets --all-features -- -D warnings
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
/// Shared display helpers for DTOs.
|
||||
///
|
||||
/// These functions centralise the mime→icon / mime→category / size→human-string
|
||||
/// logic so that every API response carries pre-computed display fields and the
|
||||
/// frontend does **not** need to duplicate these mappings.
|
||||
///
|
||||
/// The approach is: try MIME first (specific matches beat prefix matches),
|
||||
/// then fall back to the file extension when the MIME is generic
|
||||
/// (`application/octet-stream` or empty).
|
||||
// Shared display helpers for DTOs.
|
||||
//
|
||||
// These functions centralise the mime→icon / mime→category / size→human-string
|
||||
// logic so that every API response carries pre-computed display fields and the
|
||||
// frontend does **not** need to duplicate these mappings.
|
||||
//
|
||||
// The approach is: try MIME first (specific matches beat prefix matches),
|
||||
// then fall back to the file extension when the MIME is generic
|
||||
// (`application/octet-stream` or empty).
|
||||
|
||||
// ─── Private: extract lowercase extension from a filename ────────────
|
||||
|
||||
@@ -100,8 +100,6 @@ pub fn icon_class_for(name: &str, mime: &str) -> &'static str {
|
||||
|| mime.contains("typescript")
|
||||
{
|
||||
return "fas fa-file-code";
|
||||
} else if mime.starts_with("text/markdown") {
|
||||
return "fas fa-file-alt";
|
||||
} else if mime.starts_with("text/") {
|
||||
return "fas fa-file-alt";
|
||||
}
|
||||
|
||||
@@ -165,10 +165,7 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||
/// Lists every file in the subtree rooted at `folder_id`.
|
||||
///
|
||||
/// Default: falls back to `list_files(Some(folder_id))` (one level).
|
||||
async fn list_files_in_subtree(
|
||||
&self,
|
||||
folder_id: &str,
|
||||
) -> Result<Vec<FileDto>, DomainError> {
|
||||
async fn list_files_in_subtree(&self, folder_id: &str) -> Result<Vec<FileDto>, DomainError> {
|
||||
self.list_files(Some(folder_id)).await
|
||||
}
|
||||
|
||||
@@ -183,7 +180,11 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||
limit: i64,
|
||||
) -> Result<Vec<FileDto>, DomainError> {
|
||||
let all = self.list_files(folder_id).await?;
|
||||
Ok(all.into_iter().skip(offset as usize).take(limit as usize).collect())
|
||||
Ok(all
|
||||
.into_iter()
|
||||
.skip(offset as usize)
|
||||
.take(limit as usize)
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,10 +76,7 @@ pub trait FolderUseCase: Send + Sync + 'static {
|
||||
/// ordered by path. Uses ltree `<@` — single GiST-indexed query.
|
||||
///
|
||||
/// Default: returns an empty vec (stubs / mocks).
|
||||
async fn list_subtree_folders(
|
||||
&self,
|
||||
folder_id: &str,
|
||||
) -> Result<Vec<FolderDto>, DomainError> {
|
||||
async fn list_subtree_folders(&self, folder_id: &str) -> Result<Vec<FolderDto>, DomainError> {
|
||||
let _ = folder_id;
|
||||
Ok(Vec::new())
|
||||
}
|
||||
@@ -97,7 +94,11 @@ pub trait SearchUseCase: Send + Sync + 'static {
|
||||
///
|
||||
/// `user_id` identifies the authenticated user so that SQL queries filter
|
||||
/// by owner and the result cache is isolated per tenant.
|
||||
async fn search(&self, criteria: SearchCriteriaDto, user_id: &str) -> Result<SearchResultsDto, DomainError>;
|
||||
async fn search(
|
||||
&self,
|
||||
criteria: SearchCriteriaDto,
|
||||
user_id: &str,
|
||||
) -> Result<SearchResultsDto, DomainError>;
|
||||
|
||||
/// Returns quick suggestions for autocomplete (lightweight, fast).
|
||||
async fn suggest(
|
||||
|
||||
@@ -100,10 +100,7 @@ pub trait FileReadPort: Send + Sync + 'static {
|
||||
/// subtree is fetched in a single GiST-indexed query.
|
||||
///
|
||||
/// Default: falls back to `list_files(Some(folder_id))` (one level).
|
||||
async fn list_files_in_subtree(
|
||||
&self,
|
||||
folder_id: &str,
|
||||
) -> Result<Vec<File>, DomainError> {
|
||||
async fn list_files_in_subtree(&self, folder_id: &str) -> Result<Vec<File>, DomainError> {
|
||||
self.list_files(Some(folder_id)).await
|
||||
}
|
||||
|
||||
|
||||
@@ -526,7 +526,10 @@ impl AuthApplicationService {
|
||||
}
|
||||
|
||||
// Hash new password and update user
|
||||
let new_hash = self.password_hasher.hash_password(&dto.new_password).await?;
|
||||
let new_hash = self
|
||||
.password_hasher
|
||||
.hash_password(&dto.new_password)
|
||||
.await?;
|
||||
user.update_password_hash(new_hash);
|
||||
|
||||
// Save updated user
|
||||
@@ -642,10 +645,7 @@ impl AuthApplicationService {
|
||||
let admin_quota = self.capped_quota(&admin_role);
|
||||
|
||||
// Hash the password (same as register / admin_create_user)
|
||||
let password_hash = self
|
||||
.password_hasher
|
||||
.hash_password(&dto.password)
|
||||
.await?;
|
||||
let password_hash = self.password_hasher.hash_password(&dto.password).await?;
|
||||
|
||||
// Create the new admin user
|
||||
let user = User::new(
|
||||
@@ -1156,10 +1156,8 @@ impl AuthApplicationService {
|
||||
let exchange_code = hex::encode(code_bytes);
|
||||
|
||||
// Store auth response (auto-expires after 60 s via moka TTL)
|
||||
self.pending_oidc_tokens.insert(
|
||||
exchange_code.clone(),
|
||||
PendingOidcToken { auth_response },
|
||||
);
|
||||
self.pending_oidc_tokens
|
||||
.insert(exchange_code.clone(), PendingOidcToken { auth_response });
|
||||
|
||||
tracing::info!("OIDC login successful, one-time exchange code generated");
|
||||
|
||||
@@ -1169,13 +1167,16 @@ impl AuthApplicationService {
|
||||
/// Exchange a one-time code for the authentication tokens.
|
||||
/// The code is single-use and expires after 60 seconds (moka TTL).
|
||||
pub fn exchange_oidc_token(&self, one_time_code: &str) -> Result<AuthResponseDto, DomainError> {
|
||||
let pending = self.pending_oidc_tokens.remove(one_time_code).ok_or_else(|| {
|
||||
DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
"OIDC",
|
||||
"Invalid or expired exchange code. Please try logging in again.",
|
||||
)
|
||||
})?;
|
||||
let pending = self
|
||||
.pending_oidc_tokens
|
||||
.remove(one_time_code)
|
||||
.ok_or_else(|| {
|
||||
DomainError::new(
|
||||
ErrorKind::AccessDenied,
|
||||
"OIDC",
|
||||
"Invalid or expired exchange code. Please try logging in again.",
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(pending.auth_response)
|
||||
}
|
||||
|
||||
@@ -713,7 +713,10 @@ impl BatchOperationService {
|
||||
match chunk {
|
||||
Ok(bytes) => {
|
||||
if let Err(e) = zip.write_all(&bytes) {
|
||||
info!("Could not write zip chunk for {}: {}", file_dto.name, e);
|
||||
info!(
|
||||
"Could not write zip chunk for {}: {}",
|
||||
file_dto.name, e
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,10 @@ impl FileManagementUseCase for FileManagementService {
|
||||
.copy_folder_tree(source_folder_id, target_parent_id, dest_name)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Error copying folder tree (source: {}): {}", source_folder_id, e);
|
||||
error!(
|
||||
"Error copying folder tree (source: {}): {}",
|
||||
source_folder_id, e
|
||||
);
|
||||
e
|
||||
})?;
|
||||
|
||||
|
||||
@@ -88,7 +88,10 @@ impl FileRetrievalService {
|
||||
return None;
|
||||
}
|
||||
let format = OutputFormat::WebP;
|
||||
match transcode.get_transcoded(id, content.clone(), mime, format).await {
|
||||
match transcode
|
||||
.get_transcoded(id, content.clone(), mime, format)
|
||||
.await
|
||||
{
|
||||
Ok((transcoded, webp_mime, true)) => {
|
||||
debug!(
|
||||
"🖼️ WebP transcode: {} -> {} bytes ({:.0}% smaller)",
|
||||
@@ -200,12 +203,7 @@ impl FileRetrievalService {
|
||||
let etag: Arc<str> = format!("\"{}-{}\"", id, modified_at).into();
|
||||
let ct: Arc<str> = Arc::from(&*mime_type);
|
||||
cache
|
||||
.put(
|
||||
id.to_string(),
|
||||
content_bytes.clone(),
|
||||
etag,
|
||||
ct,
|
||||
)
|
||||
.put(id.to_string(), content_bytes.clone(), etag, ct)
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -310,10 +308,7 @@ impl FileRetrievalUseCase for FileRetrievalService {
|
||||
self.file_read.get_file_range_stream(id, start, end).await
|
||||
}
|
||||
|
||||
async fn list_files_in_subtree(
|
||||
&self,
|
||||
folder_id: &str,
|
||||
) -> Result<Vec<FileDto>, DomainError> {
|
||||
async fn list_files_in_subtree(&self, folder_id: &str) -> Result<Vec<FileDto>, DomainError> {
|
||||
let files = self.file_read.list_files_in_subtree(folder_id).await?;
|
||||
Ok(files.into_iter().map(FileDto::from).collect())
|
||||
}
|
||||
@@ -324,7 +319,10 @@ impl FileRetrievalUseCase for FileRetrievalService {
|
||||
offset: i64,
|
||||
limit: i64,
|
||||
) -> Result<Vec<FileDto>, DomainError> {
|
||||
let files = self.file_read.list_files_batch(folder_id, offset, limit).await?;
|
||||
let files = self
|
||||
.file_read
|
||||
.list_files_batch(folder_id, offset, limit)
|
||||
.await?;
|
||||
Ok(files.into_iter().map(FileDto::from).collect())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,10 +182,7 @@ impl FolderUseCase for FolderService {
|
||||
Ok(FolderDto::from(folder))
|
||||
}
|
||||
|
||||
async fn list_subtree_folders(
|
||||
&self,
|
||||
folder_id: &str,
|
||||
) -> Result<Vec<FolderDto>, DomainError> {
|
||||
async fn list_subtree_folders(&self, folder_id: &str) -> Result<Vec<FolderDto>, DomainError> {
|
||||
let folders = self.folder_storage.list_subtree_folders(folder_id).await?;
|
||||
Ok(folders.into_iter().map(FolderDto::from).collect())
|
||||
}
|
||||
|
||||
@@ -347,19 +347,15 @@ impl SearchUseCase for SearchService {
|
||||
let start_idx = criteria.offset.min(total_count);
|
||||
let end_idx = (criteria.offset + criteria.limit).min(total_count);
|
||||
|
||||
let mut paginated_folders = Vec::new();
|
||||
let mut paginated_files = Vec::new();
|
||||
let folder_start = start_idx.min(folder_count);
|
||||
let folder_end = end_idx.min(folder_count);
|
||||
let paginated_folders = enriched_folders[folder_start..folder_end].to_vec();
|
||||
|
||||
for i in start_idx..end_idx {
|
||||
if i < folder_count {
|
||||
paginated_folders.push(enriched_folders[i].clone());
|
||||
} else {
|
||||
let file_idx = i - folder_count;
|
||||
if file_idx < enriched_files.len() {
|
||||
paginated_files.push(enriched_files[file_idx].clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
let file_start = start_idx.saturating_sub(folder_count);
|
||||
let file_end = end_idx
|
||||
.saturating_sub(folder_count)
|
||||
.min(enriched_files.len());
|
||||
let paginated_files = enriched_files[file_start..file_end].to_vec();
|
||||
|
||||
let elapsed_ms = start.elapsed().as_millis() as u64;
|
||||
|
||||
@@ -413,12 +409,10 @@ impl SearchUseCase for SearchService {
|
||||
// ── Sort folders (files already sorted by SQL ORDER BY) ──
|
||||
match criteria.sort_by.as_str() {
|
||||
"name" => {
|
||||
enriched_folders
|
||||
.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
|
||||
enriched_folders.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
|
||||
}
|
||||
"name_desc" => {
|
||||
enriched_folders
|
||||
.sort_by(|a, b| b.name.to_lowercase().cmp(&a.name.to_lowercase()));
|
||||
enriched_folders.sort_by(|a, b| b.name.to_lowercase().cmp(&a.name.to_lowercase()));
|
||||
}
|
||||
"date" => {
|
||||
enriched_folders.sort_by(|a, b| a.modified_at.cmp(&b.modified_at));
|
||||
@@ -437,19 +431,15 @@ impl SearchUseCase for SearchService {
|
||||
let start_idx = criteria.offset.min(total_count);
|
||||
let end_idx = (criteria.offset + criteria.limit).min(total_count);
|
||||
|
||||
let mut paginated_folders = Vec::new();
|
||||
let mut paginated_files = Vec::new();
|
||||
let folder_start = start_idx.min(folder_count);
|
||||
let folder_end = end_idx.min(folder_count);
|
||||
let paginated_folders = enriched_folders[folder_start..folder_end].to_vec();
|
||||
|
||||
for i in start_idx..end_idx {
|
||||
if i < folder_count {
|
||||
paginated_folders.push(enriched_folders[i].clone());
|
||||
} else {
|
||||
let file_idx = i - folder_count;
|
||||
if file_idx < enriched_files.len() {
|
||||
paginated_files.push(enriched_files[file_idx].clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
let file_start = start_idx.saturating_sub(folder_count);
|
||||
let file_end = end_idx
|
||||
.saturating_sub(folder_count)
|
||||
.min(enriched_files.len());
|
||||
let paginated_files = enriched_files[file_start..file_end].to_vec();
|
||||
|
||||
let elapsed_ms = start.elapsed().as_millis() as u64;
|
||||
|
||||
@@ -496,7 +486,11 @@ impl SearchService {
|
||||
|
||||
#[async_trait]
|
||||
impl SearchUseCase for SearchServiceStub {
|
||||
async fn search(&self, _criteria: SearchCriteriaDto, _user_id: &str) -> Result<SearchResultsDto> {
|
||||
async fn search(
|
||||
&self,
|
||||
_criteria: SearchCriteriaDto,
|
||||
_user_id: &str,
|
||||
) -> Result<SearchResultsDto> {
|
||||
Ok(SearchResultsDto::empty())
|
||||
}
|
||||
|
||||
|
||||
@@ -132,8 +132,9 @@ impl ShareService {
|
||||
/// concurrently. This keeps RAM usage predictable (~19 MB × 2 = ~38 MB max)
|
||||
/// and avoids starving the Tokio blocking thread pool.
|
||||
async fn hash_password_async(&self, password: &str) -> Result<String, DomainError> {
|
||||
let _permit = self.hash_semaphore.acquire().await
|
||||
.map_err(|_| DomainError::internal_error("ShareService", "Hash semaphore closed".to_string()))?;
|
||||
let _permit = self.hash_semaphore.acquire().await.map_err(|_| {
|
||||
DomainError::internal_error("ShareService", "Hash semaphore closed".to_string())
|
||||
})?;
|
||||
self.password_hasher.hash_password(password).await
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user