From 63604e1cd780314b6dc6be5186bd96f7026e6543 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Sun, 10 Dec 2023 19:16:46 -0800 Subject: [PATCH] Add GitHub Actions workflow for CI This also fixes clippy warnings (or at least hides them). Signed-off-by: John Nunley --- .github/workflows/ci.yml | 72 ++++++++++++++++++++++++++++++++++++++++ src/geometry.rs | 2 ++ src/mask.rs | 5 ++- src/path_builder.rs | 20 ++++++----- src/raster.rs | 25 +++++++++----- src/scratch.rs | 6 ++-- src/segment.rs | 19 +++++++++-- src/stroke.rs | 65 ++++++++++++++++++------------------ src/style.rs | 6 ++-- src/svg_parser.rs | 1 + src/traversal.rs | 10 +++--- 11 files changed, 166 insertions(+), 65 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8f468b3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,72 @@ +name: CI + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - main + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_GIT_FETCH_WITH_CLI: true + CARGO_NET_RETRY: 10 + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + RUSTUP_MAX_RETRIES: 10 + +defaults: + run: + shell: bash + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + rust: [nightly, beta, stable] + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} + - run: rustup target add wasm32-unknown-unknown + - run: rustup target add thumbv7m-none-eabi + - name: Install WASM Test Tools and cargo-hack + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack,wasm-pack + - run: cargo test + - run: cargo test --no-default-features --features libm,eval + - run: cargo hack build --all --target thumbv7m-none-eabi \ + --no-default-features --features libm,eval --no-dev-deps + - run: cargo check --all --all-features --all-targets --target wasm32-unknown-unknown + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - run: cargo clippy --all-features --all-targets + + fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - run: cargo fmt --all --check + + doc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - run: cargo doc --all --all-features diff --git a/src/geometry.rs b/src/geometry.rs index 7f3c3ab..8421355 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -1,5 +1,6 @@ //! Geometric primitives. +#[allow(unused)] use crate::F32Ext; use core::borrow::Borrow; @@ -551,6 +552,7 @@ impl Bounds { pub(super) struct BoundsBuilder { pub count: usize, + #[allow(dead_code)] pub start: Point, pub current: Point, pub min: Point, diff --git a/src/mask.rs b/src/mask.rs index 42d2e17..6f78c13 100644 --- a/src/mask.rs +++ b/src/mask.rs @@ -4,6 +4,8 @@ use super::geometry::{Origin, Placement, Transform, Vector}; use super::path_data::{apply, PathData}; use super::scratch::Scratch; use super::style::{Fill, Style}; +#[allow(unused)] +use super::F32Ext; use crate::lib::Vec; use core::cell::RefCell; @@ -247,6 +249,7 @@ where } } +#[allow(clippy::needless_lifetimes)] pub fn render<'a, 'c, D>( mask: &'a Mask<'a, 'c, D>, offset: Vector, @@ -293,7 +296,7 @@ pub fn render<'a, 'c, D>( w, h, &mut |r| { - inner.apply(data.clone(), &style, transform, r); + inner.apply(data, &style, transform, r); }, fill, pitch, diff --git a/src/path_builder.rs b/src/path_builder.rs index 127f950..0381bd1 100644 --- a/src/path_builder.rs +++ b/src/path_builder.rs @@ -1,10 +1,14 @@ //! Path builder. +#![allow(clippy::excessive_precision)] + use super::command::Command; use super::geometry::{Angle, BoundsBuilder, Point, Transform}; +#[allow(unused)] use super::F32Ext; use crate::lib::Vec; +use core::f32; /// Describes the size of an arc. #[derive(Copy, Clone, PartialEq)] @@ -211,12 +215,11 @@ impl PathBuilder for Vec { | Command::CurveTo(_, _, p) => *p, Command::Close => { for cmd in self.iter().rev().skip(1) { - match cmd { - Command::MoveTo(p) => return *p, - _ => {} + if let Command::MoveTo(p) = cmd { + return *p; } } - return Point::ZERO; + Point::ZERO } }, } @@ -439,9 +442,9 @@ impl Arc { } let segments = ratio.ceil().max(1.); ang2 /= segments; - let a = if ang2 == 1.5707963267948966 { + let a = if ang2 == f32::consts::FRAC_PI_2 { 0.551915024494 - } else if ang2 == -1.5707963267948966 { + } else if ang2 == -f32::consts::FRAC_PI_2 { -0.551915024494 } else { 4. / 3. * (ang2 / 4.).tan() @@ -494,6 +497,7 @@ impl Iterator for Arc { } } +#[allow(clippy::too_many_arguments)] pub fn arc( sink: &mut impl PathBuilder, from: Point, @@ -568,9 +572,9 @@ pub fn arc( } let segments = ratio.ceil().max(1.); ang2 /= segments; - let a = if ang2 == 1.5707963267948966 { + let a = if ang2 == f32::consts::FRAC_PI_2 { 0.551915024494 - } else if ang2 == -1.5707963267948966 { + } else if ang2 == -f32::consts::FRAC_PI_2 { -0.551915024494 } else { 4. / 3. * (ang2 / 4.).tan() diff --git a/src/raster.rs b/src/raster.rs index 24ffe97..0361100 100644 --- a/src/raster.rs +++ b/src/raster.rs @@ -1,5 +1,7 @@ //! Path rasterizer. +#![allow(clippy::too_many_arguments)] + use super::geometry::{Point, Vector}; use super::path_builder::PathBuilder; use super::style::Fill; @@ -114,9 +116,9 @@ impl<'a, S: RasterStorage> Rasterizer<'a, S> { if index != -1 { let y = ((i as i32) - min.y) as usize; let row_offset = if y_up { - (pitch * (height - 1 - y)) as usize + pitch * (height - 1 - y) } else { - (pitch * y) as usize + pitch * y }; let row = &mut buffer[row_offset..]; let mut x = min.x; @@ -206,9 +208,9 @@ impl<'a, S: RasterStorage> Rasterizer<'a, S> { if index != -1 { let y = ((i as i32) - min.y) as usize; let row_offset = if y_up { - (pitch * (height - 1 - y)) as usize + pitch * (height - 1 - y) } else { - (pitch * y) as usize + pitch * y }; let mut x = min.x; let mut cover = 0; @@ -377,6 +379,7 @@ impl<'a, S: RasterStorage> Rasterizer<'a, S> { self.py = to_y; } + #[allow(clippy::uninit_assumed_init, invalid_value)] fn quad_to(&mut self, control: FixedPoint, to: FixedPoint) { let mut arc: [FixedPoint; 16 * 2 + 1] = unsafe { core::mem::MaybeUninit::uninit().assume_init() }; @@ -428,6 +431,7 @@ impl<'a, S: RasterStorage> Rasterizer<'a, S> { } } + #[allow(clippy::uninit_assumed_init, invalid_value)] fn curve_to(&mut self, control1: FixedPoint, control2: FixedPoint, to: FixedPoint) { let mut arc: [FixedPoint; 16 * 8 + 1] = unsafe { core::mem::MaybeUninit::uninit().assume_init() }; @@ -589,6 +593,7 @@ impl RasterStorage for HeapStorage { } #[inline(always)] + #[allow(clippy::comparison_chain)] fn set(&mut self, x: i32, y: i32, area: i32, cover: i32) { let yindex = (y - self.min.y) as usize; let mut cell_index = self.indices[yindex]; @@ -607,7 +612,7 @@ impl RasterStorage for HeapStorage { } let new_index = self.cells.len(); let cell = Cell { - x: x, + x, area, cover, next: cell_index, @@ -636,6 +641,7 @@ pub struct AdaptiveStorage { } impl AdaptiveStorage { + #[allow(clippy::uninit_assumed_init, invalid_value)] pub fn new() -> Self { Self { min: FixedPoint::default(), @@ -661,7 +667,7 @@ impl RasterStorage for AdaptiveStorage { if self.height > MAX_BAND { self.heap_indices.resize((max.y - min.y) as usize, -1); } else { - for i in 0..self.height as usize { + for i in 0..self.height { self.indices[i] = -1; } } @@ -684,6 +690,7 @@ impl RasterStorage for AdaptiveStorage { } #[inline(always)] + #[allow(clippy::comparison_chain)] fn set(&mut self, x: i32, y: i32, area: i32, cover: i32) { let yindex = (y - self.min.y) as usize; let indices = if self.height > MAX_BAND { @@ -691,7 +698,7 @@ impl RasterStorage for AdaptiveStorage { } else { &mut self.indices[..] }; - let cells = if self.heap_cells.len() != 0 { + let cells = if !self.heap_cells.is_empty() { &mut self.heap_cells[..] } else { &mut self.cells[..] @@ -713,7 +720,7 @@ impl RasterStorage for AdaptiveStorage { let new_index = self.cell_count; self.cell_count += 1; let cell = Cell { - x: x, + x, area, cover, next: cell_index, @@ -726,7 +733,7 @@ impl RasterStorage for AdaptiveStorage { if new_index < MAX_CELLS { cells[new_index] = cell; } else { - if self.heap_cells.len() == 0 { + if self.heap_cells.is_empty() { self.heap_cells.extend_from_slice(&self.cells); } self.heap_cells.push(cell); diff --git a/src/scratch.rs b/src/scratch.rs index 092401f..62946f5 100644 --- a/src/scratch.rs +++ b/src/scratch.rs @@ -79,7 +79,7 @@ impl Inner { let mut transform_sink = TransformSink { sink, transform }; stroke_with_storage( data.commands(), - &stroke, + stroke, &mut transform_sink, &mut self.segments, ); @@ -87,13 +87,13 @@ impl Inner { stroke_with_storage( data.commands() .map(|cmd| cmd.borrow().transform(&transform)), - &stroke, + stroke, sink, &mut self.segments, ); } } else { - stroke_with_storage(data.commands(), &stroke, sink, &mut self.segments); + stroke_with_storage(data.commands(), stroke, sink, &mut self.segments); } Fill::NonZero } diff --git a/src/segment.rs b/src/segment.rs index ad940cb..04e73f0 100644 --- a/src/segment.rs +++ b/src/segment.rs @@ -1,10 +1,14 @@ //! Path segmentation. +#![allow(clippy::excessive_precision)] + use super::command::Command; use super::geometry::*; +#[allow(unused)] use super::F32Ext; use core::borrow::Borrow; +use core::f32; /// Represents the time parameter for a specific distance along /// a segment. @@ -36,11 +40,13 @@ impl Line { } /// Returns a slice of the line segment described by the specified start and end times. + #[allow(unused)] pub fn slice(&self, start: f32, end: f32) -> Self { let dir = self.b - self.a; Self::new(self.a + dir * start, self.a + dir * end) } + #[allow(unused)] pub fn time(&self, distance: f32) -> SegmentTime { let len = (self.b - self.a).length(); if distance > len { @@ -55,6 +61,7 @@ impl Line { } } + #[allow(unused)] pub fn reverse(&self) -> Self { Self::new(self.b, self.a) } @@ -145,12 +152,14 @@ impl Curve { } /// Returns a curve with the direction reversed. + #[allow(unused)] pub fn reverse(&self) -> Self { Self::new(self.d, self.c, self.b, self.a) } /// Returns the time parameter for the specified linear distance along /// the curve. + #[allow(unused)] pub fn time(&self, distance: f32, tolerance: f32) -> SegmentTime { let (distance, time) = self.time_impl(distance, tolerance, 1., 0); SegmentTime { distance, time } @@ -175,6 +184,7 @@ impl Curve { + (self.d * (t * t * t)) } + #[allow(clippy::wrong_self_convention)] fn to_segment(&self, id: SegmentId) -> Option { if self.is_line(MERGE_EPSILON) { if self.a.nearly_eq_by(self.d, MERGE_EPSILON) { @@ -311,7 +321,7 @@ impl Curve { let normal_ab = normal(self.a, self.b); let normal_bc = normal(self.b, self.c); fn too_curvy(n0: Vector, n1: Vector) -> bool { - const FLAT_ENOUGH: f32 = 1.41421356237 / 2. + 1. / 10.; + const FLAT_ENOUGH: f32 = f32::consts::SQRT_2 / 2. + 1. / 10.; n0.dot(n1) <= FLAT_ENOUGH } too_curvy(normal_ab, normal_bc) || too_curvy(normal_bc, normal(self.c, self.d)) @@ -346,6 +356,7 @@ impl Segment { } } + #[allow(unused)] pub fn slice(&self, start: f32, end: f32) -> Self { match self { Self::Line(id, line) => Self::Line(*id, line.slice(start, end)), @@ -354,6 +365,7 @@ impl Segment { } } + #[allow(unused)] pub fn reverse(&self) -> Self { match self { Self::Line(id, line) => Self::Line(*id, line.reverse()), @@ -362,6 +374,7 @@ impl Segment { } } + #[allow(unused)] pub fn time(&self, distance: f32, tolerance: f32) -> SegmentTime { match self { Self::Line(_, line) => line.time(distance), @@ -373,6 +386,7 @@ impl Segment { } } + #[allow(unused)] pub fn point_normal(&self, time: f32) -> (Point, Vector) { match self { Self::Line(_, line) => { @@ -450,6 +464,7 @@ where } } + #[allow(clippy::needless_range_loop)] fn split_curve(&mut self, id: SegmentId, c: &Curve) -> Option { if c.is_line(MERGE_EPSILON) { if c.a.nearly_eq_by(c.d, MERGE_EPSILON) { @@ -489,7 +504,7 @@ where } self.split_count = i; self.split_index = 1; - return self.splits[0].to_segment(id); + self.splits[0].to_segment(id) } fn inc_id(&mut self) { diff --git a/src/stroke.rs b/src/stroke.rs index 4b4747f..723578f 100644 --- a/src/stroke.rs +++ b/src/stroke.rs @@ -1,10 +1,14 @@ //! Stroking and dashing of paths. +#![allow(clippy::needless_lifetimes)] + use super::command::Command; use super::geometry::*; use super::path_builder::*; use super::segment::*; use super::style::*; +#[allow(unused)] +use super::F32Ext; use crate::lib::Vec; use core::borrow::Borrow; @@ -17,7 +21,7 @@ where let mut stroker = Stroker::new(segments(commands, true), sink, style); let (dashes, dash_offset, empty_gaps) = validate_dashes(style.dashes, style.offset); let mut segment_buf = SmallBuf::new(); - if dashes.len() > 0 { + if !dashes.is_empty() { stroker.dash(&mut segment_buf, dashes, dash_offset, empty_gaps); } else { stroker.stroke(&mut segment_buf); @@ -35,7 +39,7 @@ pub fn stroke_with_storage<'a, I>( { let mut stroker = Stroker::new(segments(commands, true), sink, style); let (dashes, dash_offset, empty_gaps) = validate_dashes(style.dashes, style.offset); - if dashes.len() > 0 { + if !dashes.is_empty() { stroker.dash(storage, dashes, dash_offset, empty_gaps); } else { stroker.stroke(storage); @@ -163,13 +167,11 @@ where self.add_end_cap(last_point, start, last_dir); } is_first = false; + } else if id != last_id { + self.add_join(last_point, start, pivot, last_dir, segment.start_normal); } else { - if id != last_id { - self.add_join(last_point, start, pivot, last_dir, segment.start_normal); - } else { - self.add_split_join(last_point, start, pivot, last_dir, segment.start_normal); - } - } + self.add_split_join(last_point, start, pivot, last_dir, segment.start_normal); + }; last_id = id; last_dir = segment.end_normal; pivot = segment.end_pivot; @@ -181,6 +183,7 @@ where self.sink.close(); } + #[allow(clippy::field_reassign_with_default)] fn dash( &mut self, segment_buf: &mut impl StrokerStorage, @@ -256,13 +259,11 @@ where self.sink.move_to(start); first_point = start; is_first = false; + } else if id != last_id { + self.add_join(last_point, start, pivot, last_dir, segment.start_normal); } else { - if id != last_id { - self.add_join(last_point, start, pivot, last_dir, segment.start_normal); - } else { - self.add_split_join(last_point, start, pivot, last_dir, segment.start_normal); - } - } + self.add_split_join(last_point, start, pivot, last_dir, segment.start_normal); + }; last_id = id; pivot = segment.end_pivot; last_dir = segment.end_normal; @@ -285,13 +286,11 @@ where if is_first { self.add_end_cap(last_point, start, last_dir); is_first = false; + } else if id != last_id { + self.add_join(last_point, start, pivot, last_dir, segment.start_normal); } else { - if id != last_id { - self.add_join(last_point, start, pivot, last_dir, segment.start_normal); - } else { - self.add_split_join(last_point, start, pivot, last_dir, segment.start_normal); - } - } + self.add_split_join(last_point, start, pivot, last_dir, segment.start_normal); + }; last_id = id; pivot = segment.end_pivot; last_dir = segment.end_normal; @@ -335,13 +334,13 @@ where match self.join { Join::Bevel => { self.sink.line_to(to); - return to; + to } Join::Round => { let r = self.radius_abs; let (size, sweep) = (ArcSize::Small, ArcSweep::Positive); arc(self.sink, from, r, r, 0., size, sweep, to); - return to; + to } Join::Miter => { let inv_limit = self.inv_miter_limit; @@ -349,13 +348,13 @@ where let sin_half = ((1. + dot) * 0.5).sqrt(); if dot < 0.0 || sin_half < inv_limit { self.sink.line_to(to); - return to; + to } else { let mid = (from_normal + to_normal).normalize() * (self.radius / sin_half); let p = pivot + mid; self.sink.line_to(p); self.sink.line_to(to); - return to; + to } } } @@ -380,7 +379,7 @@ where let r = self.radius_abs; let (size, sweep) = (ArcSize::Small, ArcSweep::Positive); arc(self.sink, from, r, r, 0., size, sweep, to); - return to; + to } fn add_cap(&mut self, from: Point, to: Point, dir: Vector, cap: Cap) { @@ -588,7 +587,7 @@ impl Dasher { self.trange = (t0, t1); return DashOp::Emit; } - return DashOp::Continue; + DashOp::Continue } } @@ -636,7 +635,7 @@ fn validate_dashes(dashes: &[f32], offset: f32) -> (&[f32], f32, bool) { return (dashes, offset, empty_gaps); } } - return (&[], 0., false); + (&[], 0., false) } #[inline(always)] @@ -824,15 +823,15 @@ impl SmallBuf { pub fn data(&self) -> &[T] { match self { - &Self::Array(ref buf, len) => &buf[..len], - &Self::Vec(ref buf) => &buf, + Self::Array(ref buf, len) => &buf[..*len], + Self::Vec(ref buf) => buf, } } pub fn push(&mut self, value: T) { match self { - &mut Self::Vec(ref mut buf) => buf.push(value), - &mut Self::Array(ref mut buf, ref mut len) => { + Self::Vec(ref mut buf) => buf.push(value), + Self::Array(ref mut buf, ref mut len) => { if *len == MAX_SMALL_BUF { let mut vec = Vec::from(&buf[..]); vec.push(value); @@ -847,8 +846,8 @@ impl SmallBuf { pub fn clear(&mut self) { match self { - &mut Self::Array(_, ref mut len) => *len = 0, - &mut Self::Vec(ref mut buf) => buf.clear(), + Self::Array(_, ref mut len) => *len = 0, + Self::Vec(ref mut buf) => buf.clear(), } } } diff --git a/src/style.rs b/src/style.rs index d7f73e5..4f426ec 100644 --- a/src/style.rs +++ b/src/style.rs @@ -69,6 +69,7 @@ impl Default for Stroke<'_> { impl<'a> Stroke<'a> { /// Creates a new stroke style with the specified width. + #[allow(clippy::field_reassign_with_default)] pub fn new(width: f32) -> Self { let mut s = Self::default(); s.width = width; @@ -142,10 +143,7 @@ impl Default for Style<'_> { impl Style<'_> { /// Returns true if the style is a stroke. pub fn is_stroke(&self) -> bool { - match self { - Self::Stroke(_) => true, - _ => false, - } + matches!(self, Self::Stroke(_)) } } diff --git a/src/svg_parser.rs b/src/svg_parser.rs index 4ac6f0e..711787e 100644 --- a/src/svg_parser.rs +++ b/src/svg_parser.rs @@ -611,6 +611,7 @@ impl<'a> SvgCommands<'a> { } } + #[allow(clippy::match_like_matches_macro)] fn skip_whitespace(&mut self) { while self.accept_by(|b| match b { 0x9 | 0x20 | 0xA | 0xC | 0xD => true, diff --git a/src/traversal.rs b/src/traversal.rs index 4256e9f..0997e9e 100644 --- a/src/traversal.rs +++ b/src/traversal.rs @@ -78,27 +78,27 @@ where match self.segments.next()?.borrow() { End(closed) => { self.is_first = true; - return Some(Vertex::End(self.prev_dir, self.prev_point, *closed)); + Some(Vertex::End(self.prev_dir, self.prev_point, *closed)) } segment => { let (start, in_dir, out_dir, end) = get_components(segment); self.prev_dir = out_dir; self.prev_point = end; - return Some(Vertex::Start(start, in_dir)); + Some(Vertex::Start(start, in_dir)) } } } else { match self.segments.next()?.borrow() { End(closed) => { self.is_first = true; - return Some(Vertex::End(self.prev_dir, self.prev_point, *closed)); + Some(Vertex::End(self.prev_dir, self.prev_point, *closed)) } segment => { let (start, in_dir, out_dir, end) = get_components(segment); let prev_dir = self.prev_dir; self.prev_dir = out_dir; self.prev_point = end; - return Some(Vertex::Middle(prev_dir, start, in_dir)); + Some(Vertex::Middle(prev_dir, start, in_dir)) } } } @@ -224,7 +224,7 @@ where } fn next_segment(&mut self) -> Option { - while let Some(s) = self.iter.next() { + for s in self.iter.by_ref() { match s { Segment::End(..) => continue, _ => return Some(s),