From 51027324e1a33ac6a8dcb7c8a3c4875d6a14d31c Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Sun, 4 Oct 2026 11:56:00 -0400 Subject: [PATCH 1/2] Never crash: UI and app shell have no expect/unreachable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each "checked" `expect` becomes `let … else`, `if let` or `?`, with the obvious fallback (the dialog closes, the command is ignored, the view refresh is skipped). ui-egui and the three apps now deny the panic lints. The Windows `build.rs` reports a failed resource step with `cargo::error` instead of panicking. Co-Authored-By: Claude Opus 5.5 --- apps/printcraft-cli/src/main.rs | 2 ++ apps/printcraft-web/src/main.rs | 2 ++ apps/printcraft/build.rs | 3 ++- apps/printcraft/src/main.rs | 2 ++ crates/ui-egui/src/commands.rs | 25 +++++++++++++++---------- crates/ui-egui/src/comments.rs | 7 ++++--- crates/ui-egui/src/comments_panel.rs | 8 ++++---- crates/ui-egui/src/content_ui.rs | 2 +- crates/ui-egui/src/control.rs | 6 +++++- crates/ui-egui/src/create_ui.rs | 2 +- crates/ui-egui/src/editing.rs | 18 +++++++++++------- crates/ui-egui/src/export_ui.rs | 3 ++- crates/ui-egui/src/files.rs | 4 ++-- crates/ui-egui/src/fill_sign.rs | 4 ++-- crates/ui-egui/src/forms_ui.rs | 4 ++-- crates/ui-egui/src/lib.rs | 4 +++- crates/ui-egui/src/panels.rs | 5 +++-- crates/ui-egui/src/prepare.rs | 3 ++- crates/ui-egui/src/redact_ui.rs | 10 +++++----- crates/ui-egui/src/sign_ui.rs | 20 ++++++++++---------- 20 files changed, 80 insertions(+), 54 deletions(-) diff --git a/apps/printcraft-cli/src/main.rs b/apps/printcraft-cli/src/main.rs index 81f7a8ca3..c0ff2e4a5 100644 --- a/apps/printcraft-cli/src/main.rs +++ b/apps/printcraft-cli/src/main.rs @@ -29,6 +29,8 @@ //! *separate child process* with a wall-clock timeout, so hangs, panics and aborts in any //! dependency are observed and reported instead of taking the harness down. +#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] + use std::io::Write; use std::path::{Path, PathBuf}; use std::process::{Command, ExitCode, Stdio}; diff --git a/apps/printcraft-web/src/main.rs b/apps/printcraft-web/src/main.rs index 151683704..b96a91b72 100644 --- a/apps/printcraft-web/src/main.rs +++ b/apps/printcraft-web/src/main.rs @@ -1,6 +1,8 @@ //! PrintCraft in the browser. Build: `cd apps/printcraft-web && trunk build --release` //! (trunk generates the JS loader; no handwritten JS — plan/execution-plan.md §1). +#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] + #[cfg(target_arch = "wasm32")] fn main() { use eframe::wasm_bindgen::JsCast; diff --git a/apps/printcraft/build.rs b/apps/printcraft/build.rs index 638ee1de7..778cffc10 100644 --- a/apps/printcraft/build.rs +++ b/apps/printcraft/build.rs @@ -21,7 +21,8 @@ fn main() { .set("InternalName", "printcraft"); if let Err(e) = res.compile() { if std::env::var_os("PRINTCRAFT_REQUIRE_WINRES").is_some() { - panic!("embedding Windows resources failed: {e}"); + println!("cargo::error=embedding Windows resources failed: {e}"); + return; } println!("cargo:warning=printcraft.exe built without icon/version resources: {e}"); } diff --git a/apps/printcraft/src/main.rs b/apps/printcraft/src/main.rs index 6000d46e5..1ea2a9742 100644 --- a/apps/printcraft/src/main.rs +++ b/apps/printcraft/src/main.rs @@ -11,6 +11,8 @@ //! loopback port and writes `{"port", "token", "pid"}` to `` (owner-only permissions). //! Agents then drive it with `printcraft-cli ui --control …`. +#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] + use printcraft_ui_egui::PrintCraftApp; /// Freedesktop app id: the `.desktop` file name and the hicolor icon name. diff --git a/crates/ui-egui/src/commands.rs b/crates/ui-egui/src/commands.rs index 07406b936..637d75e49 100644 --- a/crates/ui-egui/src/commands.rs +++ b/crates/ui-egui/src/commands.rs @@ -90,8 +90,9 @@ impl PrintCraftApp { self.dialog = Some(Dialog::NumberPages); } link if printcraft_engine::links::for_command(link).is_some() => { - let url = printcraft_engine::links::for_command(link).expect("checked").url; - self.open_url(url); + if let Some(l) = printcraft_engine::links::for_command(link) { + self.open_url(l.url); + } } "bookmark.add" => self.bookmark_action(crate::panels::BmAction::New), "edit.undo" => self.undo(), @@ -119,7 +120,7 @@ impl PrintCraftApp { } "comment.list" => self.right = Some(RightPanel::Comments), tool if crate::comments::CommentTool::from_command(tool).is_some() => { - let tool = crate::comments::CommentTool::from_command(tool).expect("checked"); + let Some(tool) = crate::comments::CommentTool::from_command(tool) else { return false }; self.comment_prefs.group_tool[tool.group()] = tool; self.quick_tool = crate::QuickTool::Comment(tool); // Acrobat opens the Comments panel with the commenting tools. @@ -127,8 +128,10 @@ impl PrintCraftApp { self.right = Some(RightPanel::Comments); } // A text selection made before picking a markup tool is marked right away. - if let (Some(kind), Some(i)) = (tool.markup(), active) { - let info = &self.session.get(self.views[i].id).expect("active").info; + if let (Some(kind), Some(i)) = (tool.markup(), active) + && let Some(doc) = self.session.get(self.views[i].id) + { + let info = &doc.info; if let Some((page, quads)) = self.views[i].selection_quads(info) { self.views[i].clear_selection(); let style = self.comment_prefs.style(tool); @@ -268,8 +271,10 @@ impl PrintCraftApp { self.left = crate::LeftPanel::Tool("redact"); self.left_open = true; // A text selection made first is marked right away. - if let Some(i) = active { - let info = &self.session.get(self.views[i].id).expect("active").info; + if let Some(i) = active + && let Some(doc) = self.session.get(self.views[i].id) + { + let info = &doc.info; if let Some((page, quads)) = self.views[i].selection_quads(info) { self.views[i].clear_selection(); let author = self.comment_prefs.author.clone(); @@ -396,7 +401,7 @@ impl PrintCraftApp { } } field if crate::prepare::FieldTool::from_command(field).is_some() => { - let tool = crate::prepare::FieldTool::from_command(field).expect("checked"); + let Some(tool) = crate::prepare::FieldTool::from_command(field) else { return false }; self.quick_tool = crate::QuickTool::Field(tool); self.left = crate::LeftPanel::Tool("form"); self.left_open = true; @@ -406,7 +411,7 @@ impl PrintCraftApp { self.notify(format!("Click on the page to add a {}, or drag to set its size", tool.label().to_lowercase())); } fill if crate::fill_sign::FillTool::from_command(fill).is_some() => { - let tool = crate::fill_sign::FillTool::from_command(fill).expect("checked"); + let Some(tool) = crate::fill_sign::FillTool::from_command(fill) else { return false }; self.quick_tool = crate::QuickTool::Fill(tool); let initials = tool == crate::fill_sign::FillTool::Initials; if (tool == crate::fill_sign::FillTool::Signature && self.signature.is_none()) || (initials && self.initials.is_none()) { @@ -454,7 +459,7 @@ impl PrintCraftApp { let mut specs: Vec<&CommandSpec> = COMMANDS.iter().filter(|c| c.shortcut.is_some()).collect(); specs.sort_by_key(|c| std::cmp::Reverse(c.shortcut.map(|s| s.modifier_count()).unwrap_or(0))); for spec in specs { - let s = spec.shortcut.expect("filtered"); + let Some(s) = spec.shortcut else { continue }; if typing && !spec.in_text { continue; } diff --git a/crates/ui-egui/src/comments.rs b/crates/ui-egui/src/comments.rs index cb8ddfe5d..a51ff52b7 100644 --- a/crates/ui-egui/src/comments.rs +++ b/crates/ui-egui/src/comments.rs @@ -832,9 +832,10 @@ pub(crate) fn page_after_text(resp: &egui::Response, cx: &PageCx<'_>, view: &mut if tool.markup().is_none() || !cx.allowed || !(resp.drag_stopped() || resp.double_clicked()) { return; } - if let Some((page, quads)) = view.selection_quads(cx.info).filter(|(p, _)| *p == cx.page) { + if let Some(kind) = tool.markup() + && let Some((page, quads)) = view.selection_quads(cx.info).filter(|(p, _)| *p == cx.page) + { view.clear_selection(); - let kind = tool.markup().expect("checked"); view.pending_edit = Some(new_comment(cx, tool, Shape::TextMarkup { kind, quads }, String::new())); let _ = page; } @@ -994,7 +995,7 @@ pub(crate) fn composer(ctx: &egui::Context, view: &mut DocView, info: &DocInfo, let t = Tokens::get(ctx); let mut post = false; let mut cancel = false; - let c = view.comments.composer.as_mut().expect("checked"); + let c = view.comments.composer.as_mut()?; let title = match c.kind { ComposerKind::Note => "Sticky note", ComposerKind::TextBox => "Text box", diff --git a/crates/ui-egui/src/comments_panel.rs b/crates/ui-egui/src/comments_panel.rs index fa3908c0b..e5a66905d 100644 --- a/crates/ui-egui/src/comments_panel.rs +++ b/crates/ui-egui/src/comments_panel.rs @@ -280,15 +280,15 @@ fn card( }); egui::Frame::NONE.inner_margin(egui::Margin { left: 34, right: 0, top: 4, bottom: 0 }).show(ui, |ui| { ui.set_width(ui.available_width()); - if editing { - let (_, _, text) = view.comments.editing.as_mut().expect("checked"); + if editing && let Some((_, _, text)) = view.comments.editing.as_mut() { let r = ui.add(egui::TextEdit::multiline(text).desired_rows(2).desired_width(f32::INFINITY).id_salt(("comment-edit", key))); if !r.has_focus() && !ui.memory(|m| m.focused().is_some()) { r.request_focus(); } ui.horizontal(|ui| { - if ui.add(egui::Button::new(egui::RichText::new("Save").color(Color32::WHITE)).fill(t.accent).corner_radius(12)).clicked() { - let (page, index, text) = view.comments.editing.take().expect("checked"); + if ui.add(egui::Button::new(egui::RichText::new("Save").color(Color32::WHITE)).fill(t.accent).corner_radius(12)).clicked() + && let Some((page, index, text)) = view.comments.editing.take() + { edit = Some(Edit::SetAnnotationContents { page, index, text }); } if ui.button("Cancel").clicked() || ui.input(|i| i.key_pressed(egui::Key::Escape)) { diff --git a/crates/ui-egui/src/content_ui.rs b/crates/ui-egui/src/content_ui.rs index 4ea3305f1..0ca6b85f4 100644 --- a/crates/ui-egui/src/content_ui.rs +++ b/crates/ui-egui/src/content_ui.rs @@ -273,7 +273,7 @@ pub(crate) fn editor(ctx: &egui::Context, view: &mut DocView, info: &DocInfo, ad let zoom = xf.rect.width() / xf.pw.max(1.0); let (mut commit, mut cancel) = (false, false); egui::Area::new(egui::Id::new(("added-text", view.id.0))).order(egui::Order::Foreground).fixed_pos(r.min).show(ctx, |ui| { - let t = view.content.draft.as_mut().expect("checked"); + let Some(t) = view.content.draft.as_mut() else { return }; let [cr, cg, cb] = t.style.color.map(|v| (v.clamp(0.0, 1.0) * 255.0) as u8); let resp = ui.add( egui::TextEdit::multiline(&mut t.text) diff --git a/crates/ui-egui/src/control.rs b/crates/ui-egui/src/control.rs index d1c89518c..9ca9ad842 100644 --- a/crates/ui-egui/src/control.rs +++ b/crates/ui-egui/src/control.rs @@ -419,7 +419,8 @@ fn widget(id: NodeId, n: &accesskit::Node, depth: usize, focused: bool) -> Value "enabled": !n.is_disabled(), "clickable": n.supports_action(Action::Click), }); - let obj = w.as_object_mut().expect("object"); + let mut more = serde_json::Map::new(); + let obj = &mut more; if let Some(l) = n.label() { obj.insert("label".into(), json!(l)); } @@ -441,6 +442,9 @@ fn widget(id: NodeId, n: &accesskit::Node, depth: usize, focused: bool) -> Value if focused { obj.insert("focused".into(), json!(true)); } + if let Value::Object(m) = &mut w { + m.extend(more); + } w } diff --git a/crates/ui-egui/src/create_ui.rs b/crates/ui-egui/src/create_ui.rs index a1cef025e..e0cb428cf 100644 --- a/crates/ui-egui/src/create_ui.rs +++ b/crates/ui-egui/src/create_ui.rs @@ -84,7 +84,7 @@ impl PrintCraftApp { fn open_created_bytes(&mut self, name: &str, created: Result>, String>) -> Result<(), String> { let bytes = created?; let id = self.session.open_new(name, bytes).map_err(|e| e.to_string())?; - let info = &self.session.get(id).expect("just opened").info; + let info = &self.session.get(id).ok_or("the new document could not be opened")?.info; self.views.push(crate::DocView::new(id, info)); self.active = Some(self.views.len() - 1); Ok(()) diff --git a/crates/ui-egui/src/editing.rs b/crates/ui-egui/src/editing.rs index 49de89dd8..12f0ccda7 100644 --- a/crates/ui-egui/src/editing.rs +++ b/crates/ui-egui/src/editing.rs @@ -32,7 +32,8 @@ impl PrintCraftApp { let label = edit.label(); match self.session.apply(id, edit.clone()) { Ok(()) => { - let info = &self.session.get(id).expect("exists").info; + let Some(doc) = self.session.get(id) else { return true }; + let info = &doc.info; let view = &mut self.views[i]; match comment_page(&edit) { // Comment edits change one page: keep every other raster. @@ -84,8 +85,9 @@ impl PrintCraftApp { let result = if undo { self.session.undo(id) } else { self.session.redo(id) }; match result { Ok(label) => { - let info = &self.session.get(id).expect("exists").info; - self.views[i].document_changed(info); + if let Some(doc) = self.session.get(id) { + self.views[i].document_changed(&doc.info); + } self.views[i].comments.selected = None; self.notify(format!("{} {label}", if undo { "Undid" } else { "Redid" })); } @@ -177,8 +179,9 @@ impl PrintCraftApp { match self.session.mark_saved(id, bytes, Some(dest.clone())) { Ok(()) => { self.forget_recovery(id); - let info = &self.session.get(id).expect("exists").info; - self.views[index].document_changed(info); + if let Some(doc) = self.session.get(id) { + self.views[index].document_changed(&doc.info); + } self.notify(format!("Saved {}", short_name(&dest))); true } @@ -194,8 +197,9 @@ impl PrintCraftApp { match download(&name, &bytes) { Ok(()) => { let _ = self.session.mark_saved(id, bytes, None); - let info = &self.session.get(id).expect("exists").info; - self.views[index].document_changed(info); + if let Some(doc) = self.session.get(id) { + self.views[index].document_changed(&doc.info); + } self.notify(format!("Downloaded {name}")); true } diff --git a/crates/ui-egui/src/export_ui.rs b/crates/ui-egui/src/export_ui.rs index 2a7194ae6..bcee1e776 100644 --- a/crates/ui-egui/src/export_ui.rs +++ b/crates/ui-egui/src/export_ui.rs @@ -155,7 +155,8 @@ fn run( } let mut ex = Exporter::from_source(src); match kind { - ExportKind::AllImages => unreachable!("handled above"), + // Handled above. + ExportKind::AllImages => String::new(), ExportKind::Image => { for (k, p) in pages.iter().enumerate() { set(k, None); diff --git a/crates/ui-egui/src/files.rs b/crates/ui-egui/src/files.rs index d46af1232..a5b8766d2 100644 --- a/crates/ui-egui/src/files.rs +++ b/crates/ui-egui/src/files.rs @@ -350,8 +350,8 @@ impl PrintCraftApp { pub(crate) fn open_created(&mut self, name: &str, bytes: Arc>, message: &str) { match self.session.open_new(name, bytes) { Ok(id) => { - let info = &self.session.get(id).expect("just opened").info; - self.views.push(crate::DocView::new(id, info)); + let Some(doc) = self.session.get(id) else { return }; + self.views.push(crate::DocView::new(id, &doc.info)); self.active = Some(self.views.len() - 1); self.notify(message); } diff --git a/crates/ui-egui/src/fill_sign.rs b/crates/ui-egui/src/fill_sign.rs index 6495a8dd5..74c0a1a22 100644 --- a/crates/ui-egui/src/fill_sign.rs +++ b/crates/ui-egui/src/fill_sign.rs @@ -252,7 +252,7 @@ pub(crate) fn page_input( None => Some(FillAction::CreateInitials), }, mark => { - let mark = mark.mark().expect("marks"); + let mark = mark.mark()?; let (w, h) = if mark == FillMark::Line { (36.0, 4.0) } else { (12.0, 12.0) }; let rect = [at[0] - w / 2.0, at[1] - h / 2.0, at[0] + w / 2.0, at[1] + h / 2.0]; Some(FillAction::Edit(Box::new(new(page, Shape::Mark { rect, mark }, String::new(), author)))) @@ -280,7 +280,7 @@ pub(crate) fn type_box(ctx: &egui::Context, view: &mut DocView, info: &DocInfo, let mut commit = false; let mut cancel = false; egui::Area::new(egui::Id::new(("fill-text", view.id.0))).order(egui::Order::Foreground).fixed_pos(pos).show(ctx, |ui| { - let t = view.fill_text.as_mut().expect("checked"); + let Some(t) = view.fill_text.as_mut() else { return }; let width = ((t.text.len().max(8) as f32) * TEXT_SIZE as f32 * 0.6 * zoom).clamp(60.0, 600.0); let r = ui.add( egui::TextEdit::singleline(&mut t.text) diff --git a/crates/ui-egui/src/forms_ui.rs b/crates/ui-egui/src/forms_ui.rs index 9245f3fba..5ad40d1f5 100644 --- a/crates/ui-egui/src/forms_ui.rs +++ b/crates/ui-egui/src/forms_ui.rs @@ -247,7 +247,7 @@ pub(crate) fn overlay(ctx: &egui::Context, view: &mut DocView, info: &DocInfo, f let font = (size.unwrap_or(((w.rect[3] - w.rect[1]) as f32 * 0.6).clamp(6.0, 12.0)) * zoom).clamp(6.0, 64.0); egui::Area::new(egui::Id::new(("form-editor", view.id.0))).order(egui::Order::Foreground).fixed_pos(rect.min).show(ctx, |ui| { ui.set_min_size(rect.size()); - let fx = view.forms.focus.as_mut().expect("checked"); + let Some(fx) = view.forms.focus.as_mut() else { return }; let mut te = if multiline { egui::TextEdit::multiline(&mut fx.text) } else { egui::TextEdit::singleline(&mut fx.text) }; te = te .desired_width(rect.width() - 6.0) @@ -310,7 +310,7 @@ pub(crate) fn overlay(ctx: &egui::Context, view: &mut DocView, info: &DocInfo, f ui.set_min_width(rect.width().max(160.0)); ui.label(egui::RichText::new(&f.name).small().color(t.text_faint)); egui::ScrollArea::vertical().max_height(260.0).show(ui, |ui| { - let fx = view.forms.focus.as_mut().expect("checked"); + let Some(fx) = view.forms.focus.as_mut() else { return }; for (export, display) in &f.options { let on = if multi { fx.picked.contains(export) } else { f.value.first() == Some(export) }; if multi { diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 614720045..2656575c5 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -5,6 +5,8 @@ //! Everything here is presentation: documents, rendering and the tool catalogue live in //! `printcraft-engine`. +#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] + mod a11y_ui; mod actions_ui; pub mod canvas; @@ -600,7 +602,7 @@ impl PrintCraftApp { Err(e) => return Err(e.to_string()), }; self.password_prompt = None; - let doc = self.session.get(id).expect("just opened"); + let doc = self.session.get(id).ok_or("the document could not be opened")?; let pages = doc.info.pages.len(); // Acrobat opens straight to the Comments panel when a document has comments. if self.right.is_none() { diff --git a/crates/ui-egui/src/panels.rs b/crates/ui-egui/src/panels.rs index 43723e5f2..a92baf0fc 100644 --- a/crates/ui-egui/src/panels.rs +++ b/crates/ui-egui/src/panels.rs @@ -707,8 +707,9 @@ fn outline_item(ui: &mut egui::Ui, t: &Tokens, info: &DocInfo, item: &OutlineIte if resp.double_clicked() && cx.editable { *cx.action = Some(BmAction::StartRename(path.to_vec())); } - if cx.editable { - let i = *path.last().expect("non-empty path"); + if cx.editable + && let Some(&i) = path.last() + { let current = cx.current; resp.context_menu(|ui| { let mut pick = |ui: &mut egui::Ui, label: &str, enabled: bool, a: BmAction| { diff --git a/crates/ui-egui/src/prepare.rs b/crates/ui-egui/src/prepare.rs index bc9ca3754..42cb1a830 100644 --- a/crates/ui-egui/src/prepare.rs +++ b/crates/ui-egui/src/prepare.rs @@ -85,7 +85,8 @@ impl FieldTool { FieldTool::Radio => match join.filter(|f| f.kind == FormFieldKind::Radio) { Some(g) => { let taken: Vec<&str> = g.widgets.iter().filter_map(|w| w.on_state.as_deref()).collect(); - let export = (1..).map(|i| format!("Choice{i}")).find(|c| !taken.contains(&c.as_str())).expect("unbounded"); + let export = + (1..=u64::MAX).map(|i| format!("Choice{i}")).find(|c| !taken.contains(&c.as_str())).unwrap_or_else(|| "Choice".into()); NewField::Radio { group: Some(g.name.clone()), export } } None => NewField::Radio { group: None, export: "Choice1".into() }, diff --git a/crates/ui-egui/src/redact_ui.rs b/crates/ui-egui/src/redact_ui.rs index caf39275b..c405049e8 100644 --- a/crates/ui-egui/src/redact_ui.rs +++ b/crates/ui-egui/src/redact_ui.rs @@ -188,12 +188,12 @@ impl PrintCraftApp { self.redact_prefs.mark(p, vec![rect_quad([c[0] as f64, c[1] as f64, c[2] as f64, c[3] as f64])], &author) }) .collect(); - match edits.len() { - 0 => {} - 1 => { - self.apply_edit(edits.into_iter().next().expect("one")); + match <[Edit; 1]>::try_from(edits) { + Ok([one]) => { + self.apply_edit(one); } - _ => { + Err(edits) if edits.is_empty() => {} + Err(edits) => { self.apply_edit(Edit::Batch { label: "Mark pages for redaction".into(), edits }); } } diff --git a/crates/ui-egui/src/sign_ui.rs b/crates/ui-egui/src/sign_ui.rs index abc8b7df3..7966f318e 100644 --- a/crates/ui-egui/src/sign_ui.rs +++ b/crates/ui-egui/src/sign_ui.rs @@ -247,10 +247,10 @@ impl PrintCraftApp { .ok_or("There is no folder to save the digital ID in.")?; std::fs::create_dir_all(&dir).map_err(|e| e.to_string())?; let stem: String = d.name.trim().chars().map(|c| if c.is_alphanumeric() || c == ' ' || c == '-' { c } else { '_' }).collect(); - let path = (1..) + let path = (1..=u64::MAX) .map(|i| dir.join(if i == 1 { format!("{stem}.p12") } else { format!("{stem} {i}.p12") })) .find(|p| !p.exists()) - .expect("a free name"); + .ok_or("no free file name for the digital ID")?; std::fs::write(&path, &p12).map_err(|e| e.to_string())?; Ok(self.add_digital_id(&path.to_string_lossy(), &cert)) } @@ -327,8 +327,8 @@ impl PrintCraftApp { let Some((_, id)) = self.active_ids() else { return }; match self.session.open_revision(id, n) { Ok(new) => { - let info = &self.session.get(new).expect("just opened").info; - self.views.push(DocView::new(new, info)); + let Some(doc) = self.session.get(new) else { return }; + self.views.push(DocView::new(new, &doc.info)); self.active = Some(self.views.len() - 1); } Err(e) => self.notify(format!("Couldn't open revision {n}: {e}")), @@ -343,8 +343,8 @@ impl PrintCraftApp { let name = format!("{} (signed version)", doc.name.trim_end_matches(".pdf")); match self.session.open(format!("{name}.pdf"), None, std::sync::Arc::new(bytes), doc.password.clone().as_deref()) { Ok(new) => { - let info = &self.session.get(new).expect("just opened").info; - self.views.push(DocView::new(new, info)); + let Some(doc) = self.session.get(new) else { return }; + self.views.push(DocView::new(new, &doc.info)); self.active = Some(self.views.len() - 1); } Err(e) => self.notify(format!("Couldn't open the signed version: {e}")), @@ -381,7 +381,7 @@ fn choose(ui: &mut egui::Ui, app: &mut PrintCraftApp, t: &Tokens) -> bool { ui.label("Choose the digital ID that you want to use for signing:"); ui.add_space(6.0); let ids = app.digital_ids.clone(); - let d = app.sign_draft.as_mut().expect("checked"); + let Some(d) = app.sign_draft.as_mut() else { return true }; let mut close = false; egui::ScrollArea::vertical().max_height(220.0).show(ui, |ui| { for (i, e) in ids.iter().enumerate() { @@ -438,7 +438,7 @@ fn choose(ui: &mut egui::Ui, app: &mut PrintCraftApp, t: &Tokens) -> bool { fn configure(ui: &mut egui::Ui, app: &mut PrintCraftApp, t: &Tokens) -> bool { title(ui, "Configure a Digital ID for Signing"); - let d = app.sign_draft.as_mut().expect("checked"); + let Some(d) = app.sign_draft.as_mut() else { return true }; let mut close = false; let mut go = false; ui.radio_value(&mut d.new_id.create, false, "Use a Digital ID from a file"); @@ -516,7 +516,7 @@ fn configure(ui: &mut egui::Ui, app: &mut PrintCraftApp, t: &Tokens) -> bool { if go { let create = app.sign_draft.as_ref().is_some_and(|d| d.new_id.create); let result = if create { app.create_digital_id() } else { app.import_digital_id() }; - let d = app.sign_draft.as_mut().expect("checked"); + let Some(d) = app.sign_draft.as_mut() else { return true }; match result { Ok(i) => { d.selected = Some(i); @@ -562,7 +562,7 @@ fn preview(ui: &mut egui::Ui, t: &Tokens, name: &str, d: &SignDraft) { fn sign_as(ui: &mut egui::Ui, app: &mut PrintCraftApp, t: &Tokens) -> bool { let ids = app.digital_ids.clone(); - let d = app.sign_draft.as_mut().expect("checked"); + let Some(d) = app.sign_draft.as_mut() else { return true }; let Some(entry) = d.selected.and_then(|i| ids.get(i)).cloned() else { d.step = SignStep::Choose; return false; From e96a89c871c0dce1a49c4426b8980252e3a5497a Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Sun, 4 Oct 2026 13:35:00 -0400 Subject: [PATCH 2/2] Root clippy.toml: tests may unwrap, expect, panic and index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The same file as the steering PR (#11), so this PR stands on its own. The crate-level `#![deny(clippy::unwrap_used, …)]` applies to production code only. Co-Authored-By: Claude Opus 5.5 --- clippy.toml | 4 ++++ 1 file changed, 4 insertions(+) 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