chore(rust): clear the clippy backlog and finish the poison-tolerant lock sweep

`cargo clippy --all-targets` went from 51 warnings (23 in the lib) to zero.
Most were mechanical — needless borrows, `assert_eq!` against a bool literal,
`vec!` where an array does, `or_insert_with(Vec::new)`, a loop index used only
to index — and were applied with `clippy --fix`, then reviewed line by line.
That review caught one auto-fix that was *not* semantically neutral: dropping
the redundant `use hostname;` left its `#[cfg(target_os = "linux")]` orphaned
directly above `SERVICE_NAME`, which would have silently cfg'd the constant out
of every non-Linux build. Removed the stray attribute with the import.

Where a lint asked for a risky change rather than a better one, it is suppressed
with a comment saying why:

- `too_many_arguments` on five `#[tauri::command]` handlers and
  `ThumbnailCache::save_thumbnail` — most of the arity is `State<'_, _>`
  injection, and a parameter struct would change the IPC contract and the
  generated TypeScript for no readability gain.
- `large_enum_variant` on `PlayerStatusEvent` and `AutoplayDecision` — both are
  serde + specta wire types emitted a handful of times a second, never bulk
  allocated; boxing would have to stay invisible to the generated bindings while
  every match arm gained a deref.
- `await_holding_lock` on the `hybrid`/`offline` test modules — the guard is a
  test-only serialisation lock for the process-global `INCLUDE_CATALOG_BROWSE`
  flag, and the await it spans *is* the critical section. Each `#[tokio::test]`
  gets its own single-threaded runtime, so this is not the production deadlock
  class the lint targets; restructuring would reintroduce the flag race.

Real fixes elsewhere: `JellyfinItem::to_media_item` takes `self` by value, so it
is now `into_media_item`; the five-tuple episode row in the download commands
has a named `EpisodeRow` alias; the mpv `PropertyChange` arm matches
`name: "pause"` instead of guarding on it.

Also converted the last 27 raw `.lock().unwrap()` call sites to `lock_safe()`,
completing the `MutexSafe`/`RwLockSafe` convention. All of them turned out to be
in test modules — production code was already clean — so this is consistency
rather than a fix. The two raw locks in `utils/lock.rs` stay raw on purpose:
those tests deliberately poison a mutex to prove the helpers recover from it.

Pure refactoring: all 698 tests still pass.
This commit is contained in:
2026-08-16 23:05:13 +02:00
parent 73641e192c
commit 8500da1a42
27 changed files with 173 additions and 109 deletions
+9 -8
View File
@@ -496,7 +496,7 @@ pub(crate) async fn requeue_mistyped_video_downloads(
.collect::<Vec<_>>()
.join(", ");
let query = Query::new(&format!(
let query = Query::new(format!(
"UPDATE downloads
SET status = 'pending', stream_url = NULL, progress = 0,
bytes_downloaded = 0, started_at = NULL, completed_at = NULL
@@ -563,7 +563,7 @@ where
),
None => String::new(),
};
let rows_query = Query::new(&format!(
let rows_query = Query::new(format!(
"SELECT d.id, d.item_id,
COALESCE(
d.media_type,
@@ -753,6 +753,7 @@ pub async fn resume_queued_downloads(
mod tests {
use super::*;
use crate::storage::db_service::RusqliteService;
use crate::utils::lock::MutexSafe;
use rusqlite::Connection;
use std::sync::Mutex;
@@ -1012,14 +1013,14 @@ mod tests {
resolve_pending_download_urls(&db, "/data", None, move |item_id, media_type, _q| {
let seen = Arc::clone(&seen_c);
async move {
seen.lock().unwrap().push((item_id.clone(), media_type));
seen.lock_safe().push((item_id.clone(), media_type));
Some(format!("http://resolved/{item_id}"))
}
})
.await
.unwrap();
let seen = seen.lock().unwrap().clone();
let seen = seen.lock_safe().clone();
let of = |id: &str| {
seen.iter()
.find(|(i, _)| i == id)
@@ -1045,14 +1046,14 @@ mod tests {
resolve_pending_download_urls(&db, "/data", None, move |_id, media_type, _q| {
let seen = Arc::clone(&seen_c);
async move {
*seen.lock().unwrap() = media_type;
*seen.lock_safe() = media_type;
Some("http://x".to_string())
}
})
.await
.unwrap();
assert_eq!(*seen.lock().unwrap(), "audio");
assert_eq!(*seen.lock_safe(), "audio");
}
/// An explicit `media_type` on the row always wins over the item's type.
@@ -1069,14 +1070,14 @@ mod tests {
resolve_pending_download_urls(&db, "/data", None, move |_id, media_type, _q| {
let seen = Arc::clone(&seen_c);
async move {
*seen.lock().unwrap() = media_type;
*seen.lock_safe() = media_type;
Some("http://x".to_string())
}
})
.await
.unwrap();
assert_eq!(*seen.lock().unwrap(), "video");
assert_eq!(*seen.lock_safe(), "video");
}
/// Rows already downloaded under the audio default hold an audio-only