diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..4b6c79f --- /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/color/src/ase.rs b/crates/color/src/ase.rs index 853588b..ed592bf 100644 --- a/crates/color/src/ase.rs +++ b/crates/color/src/ase.rs @@ -26,8 +26,9 @@ struct R<'a> { impl R<'_> { fn take(&mut self, n: usize) -> Result<&[u8], AseError> { - let s = self.b.get(self.i..self.i + n).ok_or(AseError::Truncated)?; - self.i += n; + let end = self.i.checked_add(n).ok_or(AseError::Truncated)?; + let s = self.b.get(self.i..end).ok_or(AseError::Truncated)?; + self.i = end; Ok(s) } fn u16(&mut self) -> Result { diff --git a/crates/color/src/gradient.rs b/crates/color/src/gradient.rs index 6e5ffb3..c5e30a9 100644 --- a/crates/color/src/gradient.rs +++ b/crates/color/src/gradient.rs @@ -83,8 +83,10 @@ impl Gradient { return (a.color.lerp(&b.color, v), a.opacity + (b.opacity - a.opacity) * v); } } - let l = stops.last().unwrap(); - (l.color, l.opacity) + match stops.last() { + Some(l) => (l.color, l.opacity), + None => (Color::BLACK, 1.0), + } } /// Stops expanded so that midpoints are represented as explicit stops (for renderers without midpoints). pub fn expanded_stops(&self) -> Vec<(f32, Color, f32)> { diff --git a/crates/color/src/lib.rs b/crates/color/src/lib.rs index f861beb..bd5e915 100644 --- a/crates/color/src/lib.rs +++ b/crates/color/src/lib.rs @@ -2,6 +2,7 @@ //! //! Colours keep the model the user picked them in (RGB, CMYK, Gray, HSB is a UI view of RGB), so //! documents don't drift when converting back and forth. Rendering asks for [`Color::to_rgba`]. +#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] #![forbid(unsafe_code)] pub mod ase; diff --git a/crates/geom/src/edit_path.rs b/crates/geom/src/edit_path.rs index 2678ba4..d4e53bd 100644 --- a/crates/geom/src/edit_path.rs +++ b/crates/geom/src/edit_path.rs @@ -120,8 +120,10 @@ pub fn smooth(sp: &SubPath, a: At, b: At, tol: f64) -> SubPath { let mut out = tidy(head); if sp.closed { // The walk repeated the first anchor at the end. - if out.len() > 2 && (out[0].p - out[out.len() - 1].p).hypot() < 1e-6 { - let last = out.pop().expect("len"); + if out.len() > 2 + && (out[0].p - out[out.len() - 1].p).hypot() < 1e-6 + && let Some(last) = out.pop() + { out[0].h_in = last.h_in; } } diff --git a/crates/geom/src/lib.rs b/crates/geom/src/lib.rs index 91f29a2..29c9be2 100644 --- a/crates/geom/src/lib.rs +++ b/crates/geom/src/lib.rs @@ -5,6 +5,7 @@ //! handle positions. Rendering and algorithms convert to [`kurbo::BezPath`]. //! //! Coordinates are document points (1/72 in), y pointing down. +#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] #![forbid(unsafe_code)] pub mod corners; diff --git a/crates/geom/src/path.rs b/crates/geom/src/path.rs index 88b44ac..b50234a 100644 --- a/crates/geom/src/path.rs +++ b/crates/geom/src/path.rs @@ -307,13 +307,12 @@ impl PathData { PathEl::ClosePath => { if let Some(mut sp) = cur.take() { // Merge a closing anchor that duplicates the start. - if sp.anchors.len() > 1 { - let first = sp.anchors[0].p; - let lastp = sp.anchors.last().unwrap().p; - if first.distance(lastp) < 1e-7 { - let l = sp.anchors.pop().unwrap(); - sp.anchors[0].h_in = l.h_in; - } + if sp.anchors.len() > 1 + && let (Some(first), Some(last)) = (sp.anchors.first(), sp.anchors.last()) + && first.p.distance(last.p) < 1e-7 + && let Some(l) = sp.anchors.pop() + { + sp.anchors[0].h_in = l.h_in; } sp.closed = true; for a in &mut sp.anchors { @@ -341,9 +340,10 @@ impl PathData { return None; } let bp = self.to_bezpath(); - if self.anchor_count() == 1 { - let p = self.subpaths.iter().find_map(|s| s.anchors.first()).unwrap().p; - return Some(Rect::from_points(p, p)); + if self.anchor_count() == 1 + && let Some(a) = self.subpaths.iter().find_map(|s| s.anchors.first()) + { + return Some(Rect::from_points(a.p, a.p)); } Some(bp.bounding_box()) } diff --git a/crates/geom/src/pathfinder.rs b/crates/geom/src/pathfinder.rs index 6951489..75aa1af 100644 --- a/crates/geom/src/pathfinder.rs +++ b/crates/geom/src/pathfinder.rs @@ -67,14 +67,18 @@ fn from_flo(paths: &[SimpleBezierPath]) -> PathData { } let mut anchors = vec![Anchor::corner(p(start))]; for (c1, c2, end) in curves { - anchors.last_mut().expect("non-empty").h_out = p(c1); + if let Some(prev) = anchors.last_mut() { + prev.h_out = p(c1); + } let mut a = Anchor::corner(p(end)); a.h_in = p(c2); anchors.push(a); } // The last curve comes back to the start: fold its end into the first anchor. - if anchors.len() > 2 && anchors.last().expect("non-empty").p.distance(anchors[0].p) < 1e-6 { - let last = anchors.pop().expect("non-empty"); + if anchors.len() > 2 + && anchors.last().is_some_and(|l| l.p.distance(anchors[0].p) < 1e-6) + && let Some(last) = anchors.pop() + { anchors[0].h_in = last.h_in; } for a in &mut anchors {