Compare commits

...
Author SHA1 Message Date
Brandon ThomasandClaude Opus 5.5 edccc14470 Never crash: UI and app shells (ui-egui, vectorcraft, vectorcraft-web, cli perf)
Removes the remaining production unwraps in the UI (fit view, save, macOS
shortcut labels, layers rows, swatch rows) and the CLI perf budget runner
(errors are reported instead of expect), and adds the deny attribute to
ui-egui, the desktop app and the web app.

vectorcraft-cli's main.rs is left without the attribute because an open
PR (#13) edits that file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 14:51:52 -04:00
9 changed files with 39 additions and 8 deletions

No files matched your search

+19 -4
View File
@@ -43,7 +43,9 @@ pub fn synthetic(n: usize) -> Document {
if i % 3 == 1 {
node.opacity = 0.8;
}
d.insert(Some(l), usize::MAX, node).expect("insert");
if d.insert(Some(l), usize::MAX, node).is_err() {
break;
}
}
d
}
@@ -122,8 +124,17 @@ pub fn run(args: &[String]) -> Result<(), String> {
rows.push(("hit test (per click)", hits, 2.0));
let mut bytes = vec![];
let mut failed: Option<String> = None;
rows.push(("save .vectorcraft", median_ms(3, || bytes = vectorcraft_format::save_file(&doc)), 300.0));
rows.push(("open .vectorcraft", median_ms(3, || drop(vectorcraft_format::load(&bytes).expect("load"))), 300.0));
rows.push((
"open .vectorcraft",
median_ms(3, || {
if let Err(e) = vectorcraft_format::load(&bytes) {
failed = Some(format!("open .vectorcraft: {e}"));
}
}),
300.0,
));
let svg_opts = vectorcraft_svg::ExportOptions::default();
rows.push(("export SVG", median_ms(3, || drop(vectorcraft_svg::export(&doc, &svg_opts))), 500.0));
@@ -131,10 +142,14 @@ pub fn run(args: &[String]) -> Result<(), String> {
let unite = median_ms(3, || {
let mut s = vectorcraft_engine::Session::new();
s.add_document(synthetic(1000), None);
s.execute("select.all", &json!({})).expect("select");
s.execute("object.pathfinder.unite", &json!({})).expect("unite");
if let Err(e) = s.execute("select.all", &json!({})).and_then(|_| s.execute("object.pathfinder.unite", &json!({}))) {
failed = Some(format!("Pathfinder Unite: {e}"));
}
});
rows.push(("Pathfinder Unite, 1,000 paths", unite, 150.0));
if let Some(e) = failed {
return Err(e);
}
let mut over = 0;
println!(" {:<40} {:>10} {:>10}", "", "measured", "budget");
+1
View File
@@ -12,6 +12,7 @@
//! - dropped files are read asynchronously by `web::WebShell` and delivered through the inbox.
//!
//! URL query flag: `?webgl` forces the WebGL2 backend instead of WebGPU.
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
#[cfg(target_arch = "wasm32")]
mod web;
+1
View File
@@ -5,6 +5,7 @@
//! `--control <port>` (or `VECTORCRAFT_CONTROL_PORT`) starts a localhost JSON-lines control server:
//! `{"id":1,"method":"ui.inspect","params":{}}` → `{"id":1,"ok":true,"result":…}`.
//! See `vectorcraft_ui_egui::control` for the methods.
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
mod control_server;
#[cfg(target_os = "macos")]
+4
View File
@@ -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
+1 -1
View File
@@ -107,7 +107,7 @@ pub fn fit(app: &mut VectorcraftApp, how: &str) {
_ => st.doc.artboards.first().map(|a| a.rect),
};
let Some(target) = target else { return };
let v = app.view_mut().unwrap();
let Some(v) = app.view_mut() else { return };
v.center = target.center();
v.fitted = true;
if how == "view.actualSize" {
+1
View File
@@ -4,6 +4,7 @@
//! (view/window) here and everything else to the engine. The same entry point serves menus,
//! shortcuts, the ⌘K palette and the control channel ([`control`]).
#![forbid(unsafe_code)]
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
mod brand;
pub mod canvas;
+8 -1
View File
@@ -1574,7 +1574,14 @@ pub fn pretty_shortcut(s: &str) -> String {
if cfg!(target_os = "macos") {
let mut mods = String::new();
let parts: Vec<&str> = s.split('+').collect();
let (key, ms) = if s.ends_with("++") { ("+", &parts[..parts.len() - 2]) } else { (*parts.last().unwrap(), &parts[..parts.len() - 1]) };
let (key, ms) = if s.ends_with("++") {
("+", parts.get(..parts.len().saturating_sub(2)).unwrap_or_default())
} else {
match parts.split_last() {
Some((key, ms)) => (*key, ms),
None => (s, &[][..]),
}
};
for m in ["Ctrl", "Alt", "Shift", "Cmd"] {
if ms.contains(&m) {
mods.push_str(match m {
+1 -1
View File
@@ -275,7 +275,7 @@ fn row(
}
if has_children && expanded.contains(&n.id.0) {
for (i, c) in n.children().unwrap().iter().enumerate().rev() {
for (i, c) in n.children().map_or(&[][..], Vec::as_slice).iter().enumerate().rev() {
row(ui, doc, c, depth + 1, i == 0 && n.clips(), sel, current, expanded, actions, t);
}
}
+3 -1
View File
@@ -676,7 +676,9 @@ pub(crate) fn tile_grid<'a, T>(ui: &mut Ui, items: &'a [T], view: View, is_folde
if len == per_row || (len > 0 && is_folder(e)) {
rows.push(vec![]);
}
rows.last_mut().expect("a row").push(e);
if let Some(row) = rows.last_mut() {
row.push(e);
}
}
let mut out = Vec::with_capacity(items.len());
for row in rows {