From a74b1b32ff7c77966ddd59ef1f7c1f22a65cc7cd Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Sun, 4 Oct 2026 12:20:44 -0400 Subject: [PATCH] Never crash: engine panic sites outside export.rs; mcp denies panics - merge output_path: a bounded search for a free name instead of `for k in 1..` + unreachable!(). - library open: Option::insert returns the new library, no expect. - relink: a one-op batch unwraps through <[Op; 1]>::try_from, no expect. - lightcraft-mcp (already clean) now #![deny] unwrap/expect/panic/ unimplemented/todo/unreachable; clippy.toml as in the steering PR. crates/engine/src/export.rs keeps one expect (open PR #51 edits that file), so lightcraft-engine doesn't get the deny attribute yet. Co-Authored-By: Claude Opus 5.5 --- clippy.toml | 4 ++++ crates/engine/src/cmd/missing.rs | 6 +++++- crates/engine/src/library.rs | 4 ++-- crates/engine/src/merge.rs | 12 ++++-------- crates/mcp/src/lib.rs | 1 + 5 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 clippy.toml diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 000000000..4b6c79f55 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,4 @@ +allow-unwrap-in-tests = true +allow-expect-in-tests = true +allow-panic-in-tests = true +allow-indexing-slicing-in-tests = true diff --git a/crates/engine/src/cmd/missing.rs b/crates/engine/src/cmd/missing.rs index 8ec96355b..22fceda5a 100644 --- a/crates/engine/src/cmd/missing.rs +++ b/crates/engine/src/cmd/missing.rs @@ -50,7 +50,11 @@ fn relink(s: &mut Session, p: &Value) -> Result { { ops.extend(crate::cmd::convert::content_op(id, ph, info)); } - s.commit("Relink Photo", if ops.len() == 1 { ops.pop().expect("one op") } else { Op::Batch { ops } })?; + let op = match <[Op; 1]>::try_from(ops) { + Ok([op]) => op, + Err(ops) => Op::Batch { ops }, + }; + s.commit("Relink Photo", op)?; s.media.forget(id); Ok(json!({"id": id.0, "path": abs})) } diff --git a/crates/engine/src/library.rs b/crates/engine/src/library.rs index 35d465801..6644efa35 100644 --- a/crates/engine/src/library.rs +++ b/crates/engine/src/library.rs @@ -228,8 +228,8 @@ impl Session { self.media.attach_disk_cache(&dir.join("thumbs"), self.cache_bytes()); } let view_written = self.view_json(); - self.library = Some(Library { dir, on_disk, journal, files, report, last_error: None, presets_written, view_written }); - Ok(&self.library.as_ref().expect("just set").report) + let library = self.library.insert(Library { dir, on_disk, journal, files, report, last_error: None, presets_written, view_written }); + Ok(&library.report) } /// Write pending ops to the log (fsynced), compact when due, and save changed presets. diff --git a/crates/engine/src/merge.rs b/crates/engine/src/merge.rs index c7bab82f5..24c654896 100644 --- a/crates/engine/src/merge.rs +++ b/crates/engine/src/merge.rs @@ -309,14 +309,10 @@ fn output_path(first: &str, suffix: &str) -> String { let p = std::path::Path::new(first); let dir = p.parent().map(|d| d.to_path_buf()).unwrap_or_default(); let stem = p.file_stem().map(|s| s.to_string_lossy().to_string()).unwrap_or_else(|| "Merge".into()); - for k in 1.. { - let name = if k == 1 { format!("{stem}-{suffix}.dng") } else { format!("{stem}-{suffix}-{k}.dng") }; - let cand = dir.join(name); - if !cand.exists() { - return cand.to_string_lossy().to_string(); - } - } - unreachable!() + let cand = |k: u64| dir.join(if k == 1 { format!("{stem}-{suffix}.dng") } else { format!("{stem}-{suffix}-{k}.dng") }); + // (`u64::MAX` taken names can't exist; the last candidate is the fallback) + let free = (1..u64::MAX).map(cand).find(|c| !c.exists()).unwrap_or_else(|| cand(u64::MAX)); + free.to_string_lossy().to_string() } /// Parse `merge.*` command parameters into a kind and finishing options. diff --git a/crates/mcp/src/lib.rs b/crates/mcp/src/lib.rs index 2b8fa10f5..a1fb5510d 100644 --- a/crates/mcp/src/lib.rs +++ b/crates/mcp/src/lib.rs @@ -13,6 +13,7 @@ //! //! Entry points: [`Server::serve`] (stdio loop) and [`Server::handle_line`] (one message). #![forbid(unsafe_code)] +#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] mod backend; mod base64;