fix: resolve all clippy warnings for CI (async_fn_in_trait, collapsible_if, type_complexity, dead_code)
- Allow async_fn_in_trait lint crate-wide (internal project, 413 warnings) - Add integration_tests feature to Cargo.toml to fix unexpected cfg warnings - Collapse nested if statements into single conditions (13 locations) - Add type_complexity allows on pg repository functions (12 locations) - Fix dead code warnings in test modules with allow attributes - Fix E0599 by gating new_stub() for integration_tests feature - Add result_unit_err and result_large_err allows where appropriate - Apply rustfmt formatting Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -57,6 +57,7 @@ urlencoding = "2.1.3"
|
|||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
test_utils = ["mockall"]
|
test_utils = ["mockall"]
|
||||||
|
integration_tests = []
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(integration_tests)'] }
|
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(integration_tests)'] }
|
||||||
|
|||||||
@@ -1041,10 +1041,15 @@ impl BatchOperationService {
|
|||||||
|
|
||||||
#[cfg(integration_tests)]
|
#[cfg(integration_tests)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#[allow(unused_imports)]
|
||||||
use super::*;
|
use super::*;
|
||||||
|
#[allow(unused_imports)]
|
||||||
use crate::infrastructure::repositories::pg::file_blob_read_repository::FileBlobReadRepository;
|
use crate::infrastructure::repositories::pg::file_blob_read_repository::FileBlobReadRepository;
|
||||||
|
#[allow(unused_imports)]
|
||||||
use crate::infrastructure::repositories::pg::file_blob_write_repository::FileBlobWriteRepository;
|
use crate::infrastructure::repositories::pg::file_blob_write_repository::FileBlobWriteRepository;
|
||||||
|
#[allow(unused_imports)]
|
||||||
use crate::infrastructure::repositories::pg::folder_db_repository::FolderDbRepository;
|
use crate::infrastructure::repositories::pg::folder_db_repository::FolderDbRepository;
|
||||||
|
#[allow(unused_imports)]
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|||||||
@@ -131,17 +131,20 @@ impl FileReadPort for MockFileReadPort {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Minimal mock write port — only `move_file` and `rename_file` need real logic.
|
/// Minimal mock write port — only `move_file` and `rename_file` need real logic.
|
||||||
|
#[allow(dead_code)]
|
||||||
struct MockFileWritePort {
|
struct MockFileWritePort {
|
||||||
files: Mutex<HashMap<String, File>>,
|
files: Mutex<HashMap<String, File>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MockFileWritePort {
|
impl MockFileWritePort {
|
||||||
|
#[allow(dead_code)]
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
files: Mutex::new(HashMap::new()),
|
files: Mutex::new(HashMap::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn insert(&self, id: &str, name: &str) {
|
fn insert(&self, id: &str, name: &str) {
|
||||||
let file = File::new(
|
let file = File::new(
|
||||||
id.to_string(),
|
id.to_string(),
|
||||||
|
|||||||
@@ -430,9 +430,11 @@ impl ShareUseCase for ShareService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(integration_tests)]
|
#[cfg(feature = "integration_tests")]
|
||||||
|
#[allow(dead_code)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
#[allow(unused_imports)]
|
||||||
use crate::application::dtos::share_dto::SharePermissionsDto;
|
use crate::application::dtos::share_dto::SharePermissionsDto;
|
||||||
use crate::application::ports::auth_ports::PasswordHasherPort;
|
use crate::application::ports::auth_ports::PasswordHasherPort;
|
||||||
use crate::application::ports::share_ports::ShareStoragePort;
|
use crate::application::ports::share_ports::ShareStoragePort;
|
||||||
|
|||||||
@@ -197,8 +197,9 @@ where
|
|||||||
.file_write_port
|
.file_write_port
|
||||||
.restore_from_trash(&file_id, &original_path)
|
.restore_from_trash(&file_id, &original_path)
|
||||||
.await;
|
.await;
|
||||||
if let Err(e) = result {
|
if let Err(e) = result
|
||||||
if !format!("{}", e).contains("not found") {
|
&& !format!("{}", e).contains("not found")
|
||||||
|
{
|
||||||
return Err(DomainError::new(
|
return Err(DomainError::new(
|
||||||
ErrorKind::InternalError,
|
ErrorKind::InternalError,
|
||||||
"File",
|
"File",
|
||||||
@@ -206,7 +207,6 @@ where
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
TrashedItemType::Folder => {
|
TrashedItemType::Folder => {
|
||||||
let folder_id = item.original_id().to_string();
|
let folder_id = item.original_id().to_string();
|
||||||
let original_path = item.original_path().to_string();
|
let original_path = item.original_path().to_string();
|
||||||
@@ -214,20 +214,17 @@ where
|
|||||||
.folder_storage_port
|
.folder_storage_port
|
||||||
.restore_from_trash(&folder_id, &original_path)
|
.restore_from_trash(&folder_id, &original_path)
|
||||||
.await;
|
.await;
|
||||||
if let Err(e) = result {
|
if let Err(e) = result
|
||||||
if !format!("{}", e).contains("not found") {
|
&& !format!("{}", e).contains("not found")
|
||||||
|
{
|
||||||
return Err(DomainError::new(
|
return Err(DomainError::new(
|
||||||
ErrorKind::InternalError,
|
ErrorKind::InternalError,
|
||||||
"Folder",
|
"Folder",
|
||||||
format!(
|
format!("Error restoring folder {} from trash: {}", folder_id, e),
|
||||||
"Error restoring folder {} from trash: {}",
|
|
||||||
folder_id, e
|
|
||||||
),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
self.trash_repository
|
self.trash_repository
|
||||||
.restore_from_trash(&trash_uuid, &user_uuid)
|
.restore_from_trash(&trash_uuid, &user_uuid)
|
||||||
.await
|
.await
|
||||||
@@ -260,8 +257,9 @@ where
|
|||||||
TrashedItemType::File => {
|
TrashedItemType::File => {
|
||||||
let file_id = item.original_id().to_string();
|
let file_id = item.original_id().to_string();
|
||||||
let result = self.file_write_port.delete_file_permanently(&file_id).await;
|
let result = self.file_write_port.delete_file_permanently(&file_id).await;
|
||||||
if let Err(e) = result {
|
if let Err(e) = result
|
||||||
if !format!("{}", e).contains("not found") {
|
&& !format!("{}", e).contains("not found")
|
||||||
|
{
|
||||||
return Err(DomainError::new(
|
return Err(DomainError::new(
|
||||||
ErrorKind::InternalError,
|
ErrorKind::InternalError,
|
||||||
"File",
|
"File",
|
||||||
@@ -269,27 +267,23 @@ where
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
TrashedItemType::Folder => {
|
TrashedItemType::Folder => {
|
||||||
let folder_id = item.original_id().to_string();
|
let folder_id = item.original_id().to_string();
|
||||||
let result = self
|
let result = self
|
||||||
.folder_storage_port
|
.folder_storage_port
|
||||||
.delete_folder_permanently(&folder_id)
|
.delete_folder_permanently(&folder_id)
|
||||||
.await;
|
.await;
|
||||||
if let Err(e) = result {
|
if let Err(e) = result
|
||||||
if !format!("{}", e).contains("not found") {
|
&& !format!("{}", e).contains("not found")
|
||||||
|
{
|
||||||
return Err(DomainError::new(
|
return Err(DomainError::new(
|
||||||
ErrorKind::InternalError,
|
ErrorKind::InternalError,
|
||||||
"Folder",
|
"Folder",
|
||||||
format!(
|
format!("Error deleting folder {} permanently: {}", folder_id, e),
|
||||||
"Error deleting folder {} permanently: {}",
|
|
||||||
folder_id, e
|
|
||||||
),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
self.trash_repository
|
self.trash_repository
|
||||||
.delete_permanently(&trash_uuid, &user_uuid)
|
.delete_permanently(&trash_uuid, &user_uuid)
|
||||||
.await
|
.await
|
||||||
@@ -804,9 +798,13 @@ impl FolderRepository for MockFolderRepository {
|
|||||||
|
|
||||||
#[cfg(integration_tests)]
|
#[cfg(integration_tests)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#[allow(unused_imports)]
|
||||||
use super::*;
|
use super::*;
|
||||||
|
#[allow(unused_imports)]
|
||||||
use crate::application::ports::trash_ports::TrashUseCase;
|
use crate::application::ports::trash_ports::TrashUseCase;
|
||||||
|
#[allow(unused_imports)]
|
||||||
use crate::infrastructure::repositories::pg::file_blob_read_repository::FileBlobReadRepository;
|
use crate::infrastructure::repositories::pg::file_blob_read_repository::FileBlobReadRepository;
|
||||||
|
#[allow(unused_imports)]
|
||||||
use crate::infrastructure::repositories::pg::file_blob_write_repository::FileBlobWriteRepository;
|
use crate::infrastructure::repositories::pg::file_blob_write_repository::FileBlobWriteRepository;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|||||||
@@ -235,6 +235,7 @@ impl FileReadPort for FileBlobReadRepository {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
async fn list_files(&self, folder_id: Option<&str>) -> Result<Vec<File>, DomainError> {
|
async fn list_files(&self, folder_id: Option<&str>) -> Result<Vec<File>, DomainError> {
|
||||||
let rows: Vec<FileRow> = if let Some(fid) = folder_id {
|
let rows: Vec<FileRow> = if let Some(fid) = folder_id {
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
@@ -343,6 +344,7 @@ impl FileReadPort for FileBlobReadRepository {
|
|||||||
///
|
///
|
||||||
/// Uses a single SQL query with `LIMIT/OFFSET` to avoid loading the full
|
/// Uses a single SQL query with `LIMIT/OFFSET` to avoid loading the full
|
||||||
/// folder contents into memory. Ideal for streaming WebDAV PROPFIND.
|
/// folder contents into memory. Ideal for streaming WebDAV PROPFIND.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
async fn list_files_batch(
|
async fn list_files_batch(
|
||||||
&self,
|
&self,
|
||||||
folder_id: Option<&str>,
|
folder_id: Option<&str>,
|
||||||
@@ -986,6 +988,7 @@ impl FileReadPort for FileBlobReadRepository {
|
|||||||
Ok(count)
|
Ok(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
async fn suggest_files_by_name(
|
async fn suggest_files_by_name(
|
||||||
&self,
|
&self,
|
||||||
folder_id: Option<&str>,
|
folder_id: Option<&str>,
|
||||||
@@ -1061,9 +1064,11 @@ impl FileReadPort for FileBlobReadRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(integration_tests)]
|
#[cfg(feature = "integration_tests")]
|
||||||
|
#[allow(dead_code)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
#[allow(unused_imports)]
|
||||||
use crate::common::stubs::StubDedupPort;
|
use crate::common::stubs::StubDedupPort;
|
||||||
use crate::infrastructure::repositories::pg::folder_db_repository::FolderDbRepository;
|
use crate::infrastructure::repositories::pg::folder_db_repository::FolderDbRepository;
|
||||||
|
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ impl FolderRepository for FolderDbRepository {
|
|||||||
Self::row_to_folder(row.0, row.1, row.2, row.3, Some(row.4), row.5, row.6)
|
Self::row_to_folder(row.0, row.1, row.2, row.3, Some(row.4), row.5, row.6)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
async fn list_folders(&self, parent_id: Option<&str>) -> Result<Vec<Folder>, DomainError> {
|
async fn list_folders(&self, parent_id: Option<&str>) -> Result<Vec<Folder>, DomainError> {
|
||||||
let rows: Vec<FolderRow> = if let Some(pid) = parent_id {
|
let rows: Vec<FolderRow> = if let Some(pid) = parent_id {
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
@@ -236,6 +237,7 @@ impl FolderRepository for FolderDbRepository {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
async fn list_folders_by_owner(
|
async fn list_folders_by_owner(
|
||||||
&self,
|
&self,
|
||||||
parent_id: Option<&str>,
|
parent_id: Option<&str>,
|
||||||
@@ -283,6 +285,7 @@ impl FolderRepository for FolderDbRepository {
|
|||||||
/// Paginated folder listing — single query with `COUNT(*) OVER()` window
|
/// Paginated folder listing — single query with `COUNT(*) OVER()` window
|
||||||
/// function so the total matching count comes back alongside the data rows,
|
/// function so the total matching count comes back alongside the data rows,
|
||||||
/// eliminating a separate COUNT round-trip.
|
/// eliminating a separate COUNT round-trip.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
async fn list_folders_paginated(
|
async fn list_folders_paginated(
|
||||||
&self,
|
&self,
|
||||||
parent_id: Option<&str>,
|
parent_id: Option<&str>,
|
||||||
@@ -346,6 +349,7 @@ impl FolderRepository for FolderDbRepository {
|
|||||||
|
|
||||||
/// Paginated folder listing filtered by owner — single query with
|
/// Paginated folder listing filtered by owner — single query with
|
||||||
/// `COUNT(*) OVER()` to avoid a separate COUNT round-trip.
|
/// `COUNT(*) OVER()` to avoid a separate COUNT round-trip.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
async fn list_folders_by_owner_paginated(
|
async fn list_folders_by_owner_paginated(
|
||||||
&self,
|
&self,
|
||||||
parent_id: Option<&str>,
|
parent_id: Option<&str>,
|
||||||
@@ -685,6 +689,7 @@ impl FolderRepository for FolderDbRepository {
|
|||||||
///
|
///
|
||||||
/// Single GiST-indexed query: `fo.lpath <@ (root's lpath)`.
|
/// Single GiST-indexed query: `fo.lpath <@ (root's lpath)`.
|
||||||
/// Ordered by `fo.path` so callers can iterate in directory order.
|
/// Ordered by `fo.path` so callers can iterate in directory order.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
async fn list_subtree_folders(&self, folder_id: &str) -> Result<Vec<Folder>, DomainError> {
|
async fn list_subtree_folders(&self, folder_id: &str) -> Result<Vec<Folder>, DomainError> {
|
||||||
let sql = "SELECT fo.id::text, fo.name, fo.path, fo.parent_id::text, \
|
let sql = "SELECT fo.id::text, fo.name, fo.path, fo.parent_id::text, \
|
||||||
fo.user_id::text, \
|
fo.user_id::text, \
|
||||||
@@ -716,6 +721,7 @@ impl FolderRepository for FolderDbRepository {
|
|||||||
/// - Non-recursive: `WHERE parent_id = $1 AND user_id = $2 [AND LIKE]`
|
/// - Non-recursive: `WHERE parent_id = $1 AND user_id = $2 [AND LIKE]`
|
||||||
/// - Recursive + folder_id: delegates to `list_descendant_folders`
|
/// - Recursive + folder_id: delegates to `list_descendant_folders`
|
||||||
/// - Recursive + no folder_id: `WHERE user_id = $1 [AND LIKE]`
|
/// - Recursive + no folder_id: `WHERE user_id = $1 [AND LIKE]`
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
async fn search_folders(
|
async fn search_folders(
|
||||||
&self,
|
&self,
|
||||||
parent_id: Option<&str>,
|
parent_id: Option<&str>,
|
||||||
@@ -854,6 +860,7 @@ impl FolderRepository for FolderDbRepository {
|
|||||||
///
|
///
|
||||||
/// Single SQL query: `fo.lpath <@ (root's lpath)` fetches the entire
|
/// Single SQL query: `fo.lpath <@ (root's lpath)` fetches the entire
|
||||||
/// subtree in one indexed scan. Optional name filter is pushed to SQL.
|
/// subtree in one indexed scan. Optional name filter is pushed to SQL.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
async fn list_descendant_folders(
|
async fn list_descendant_folders(
|
||||||
&self,
|
&self,
|
||||||
folder_id: &str,
|
folder_id: &str,
|
||||||
@@ -902,6 +909,7 @@ impl FolderRepository for FolderDbRepository {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
async fn suggest_folders_by_name(
|
async fn suggest_folders_by_name(
|
||||||
&self,
|
&self,
|
||||||
parent_id: Option<&str>,
|
parent_id: Option<&str>,
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ impl DedupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a stub instance for testing — never hits PG or the filesystem.
|
/// Creates a stub instance for testing — never hits PG or the filesystem.
|
||||||
#[cfg(test)]
|
#[cfg(any(test, feature = "integration_tests"))]
|
||||||
pub fn new_stub() -> Self {
|
pub fn new_stub() -> Self {
|
||||||
let stub_pool = Arc::new(
|
let stub_pool = Arc::new(
|
||||||
sqlx::pool::PoolOptions::<sqlx::Postgres>::new()
|
sqlx::pool::PoolOptions::<sqlx::Postgres>::new()
|
||||||
|
|||||||
@@ -63,12 +63,13 @@ impl WebDavLockStore {
|
|||||||
///
|
///
|
||||||
/// Returns `Ok(LockEntry)` on success, or `Err(existing)` if the resource
|
/// Returns `Ok(LockEntry)` on success, or `Err(existing)` if the resource
|
||||||
/// is already exclusively locked by a different token.
|
/// is already exclusively locked by a different token.
|
||||||
pub fn acquire(&self, path: &str, info: LockInfo) -> Result<LockEntry, Box<LockEntry>> {
|
#[allow(clippy::result_large_err)]
|
||||||
|
pub fn acquire(&self, path: &str, info: LockInfo) -> Result<LockEntry, LockEntry> {
|
||||||
// Check for existing conflicting lock
|
// Check for existing conflicting lock
|
||||||
if let Some(existing) = self.by_path.get(path)
|
if let Some(existing) = self.by_path.get(path)
|
||||||
&& existing.info.scope == LockScope::Exclusive
|
&& existing.info.scope == LockScope::Exclusive
|
||||||
{
|
{
|
||||||
return Err(Box::new(existing));
|
return Err(existing);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ttl = Self::parse_timeout(info.timeout.as_deref());
|
let ttl = Self::parse_timeout(info.timeout.as_deref());
|
||||||
|
|||||||
@@ -63,8 +63,9 @@ pub async fn basic_auth_middleware(
|
|||||||
parse_basic_auth(auth_header).ok_or(NextcloudAuthError::Unauthorized)?;
|
parse_basic_auth(auth_header).ok_or(NextcloudAuthError::Unauthorized)?;
|
||||||
|
|
||||||
// Check account lockout before attempting password verification (saves CPU)
|
// Check account lockout before attempting password verification (saves CPU)
|
||||||
if let Some(auth_svc) = state.auth_service.as_ref() {
|
if let Some(auth_svc) = state.auth_service.as_ref()
|
||||||
if let Err(secs) = auth_svc.login_lockout.check(&username) {
|
&& let Err(secs) = auth_svc.login_lockout.check(&username)
|
||||||
|
{
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
username = %username,
|
username = %username,
|
||||||
lockout_remaining_secs = secs,
|
lockout_remaining_secs = secs,
|
||||||
@@ -72,7 +73,6 @@ pub async fn basic_auth_middleware(
|
|||||||
);
|
);
|
||||||
return Err(NextcloudAuthError::Unauthorized);
|
return Err(NextcloudAuthError::Unauthorized);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let nextcloud = state
|
let nextcloud = state
|
||||||
.nextcloud
|
.nextcloud
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![allow(async_fn_in_trait)]
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user