Apply some Clippy lints (#744)

Let me know if I should add these to the `Cargo.toml`.

All `cargo test` tests pass on the latest x86_64-unknown-linux-gnu
nightly.

---------

Co-authored-by: Alejandro González <me@alegon.dev>
This commit is contained in:
Luracasmus 2025-12-06 16:06:35 +01:00 committed by GitHub
parent 285b0aae10
commit 33cea5603e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 120 additions and 108 deletions

View file

@ -112,3 +112,19 @@ assets = [
["README.md", "usr/share/doc/oxipng/", "644"],
["CHANGELOG.md", "usr/share/doc/oxipng/", "644"],
]
[lints.clippy]
unnecessary_semicolon = "warn"
use_self = "warn"
ignored_unit_patterns = "warn"
unseparated_literal_suffix = "warn"
missing_const_for_fn = "warn"
redundant_closure_for_method_calls = "warn"
option_if_let_else = "warn"
redundant_clone = "warn"
semicolon_if_nothing_returned = "warn"
cloned_instead_of_copied = "warn"
if_not_else = "warn"
useless_let_if_seq = "warn"
manual_let_else = "warn"
map_unwrap_or = "warn"

View file

@ -31,11 +31,11 @@ pub struct Frame {
impl Frame {
/// Construct a new Frame from the data in a fcTL chunk
pub fn from_fctl_data(byte_data: &[u8]) -> PngResult<Frame> {
pub fn from_fctl_data(byte_data: &[u8]) -> PngResult<Self> {
if byte_data.len() < 26 {
return Err(PngError::TruncatedData);
}
Ok(Frame {
Ok(Self {
width: read_be_u32(&byte_data[4..8]),
height: read_be_u32(&byte_data[8..12]),
x_offset: read_be_u32(&byte_data[12..16]),

View file

@ -1,8 +1,9 @@
mod deflater;
use std::{fmt, fmt::Display};
pub use deflater::{crc32, deflate, inflate};
use crate::{PngError, PngResult};
use std::{fmt, fmt::Display};
#[cfg(feature = "zopfli")]
mod zopfli_oxipng;

View file

@ -5,9 +5,9 @@ pub fn deflate(data: &[u8], options: zopfli::Options) -> PngResult<Vec<u8>> {
// Since Rust v1.74, passing &[u8] directly into zopfli causes a regression in compressed size
// for some files. Wrapping the slice in another Read implementer such as Box fixes it for now.
match zopfli::compress(options, zopfli::Format::Zlib, Box::new(data), &mut output) {
Ok(_) => (),
Ok(()) => (),
Err(_) => return Err(PngError::new("Failed to compress in zopfli")),
};
}
output.shrink_to_fit();
Ok(output)
}

View file

@ -28,36 +28,34 @@ impl fmt::Display for PngError {
#[cold]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
PngError::APNGOutOfOrder => f.write_str("APNG chunks are out of order"),
PngError::C2PAMetadataPreventsChanges => f.write_str(
Self::APNGOutOfOrder => f.write_str("APNG chunks are out of order"),
Self::C2PAMetadataPreventsChanges => f.write_str(
"The image contains C2PA manifest that would be invalidated by any file changes",
),
PngError::ChunkMissing(s) => write!(f, "Chunk {s} missing or empty"),
PngError::CRCMismatch(ref c) => write!(
Self::ChunkMissing(s) => write!(f, "Chunk {s} missing or empty"),
Self::CRCMismatch(ref c) => write!(
f,
"CRC mismatch in {} chunk; May be recoverable by using --fix",
String::from_utf8_lossy(c)
),
PngError::DeflatedDataTooLong(_) => f.write_str("Deflated data too long"),
PngError::IncorrectDataLength(l1, l2) => write!(
Self::DeflatedDataTooLong(_) => f.write_str("Deflated data too long"),
Self::IncorrectDataLength(l1, l2) => write!(
f,
"Data length {l1} does not match the expected length {l2}"
),
PngError::InflatedDataTooLong(max) => write!(
Self::InflatedDataTooLong(max) => write!(
f,
"Inflated data would exceed the maximum size ({max} bytes)"
),
PngError::InvalidData => f.write_str("Invalid data found; unable to read PNG file"),
PngError::InvalidDepthForType(d, ref c) => {
Self::InvalidData => f.write_str("Invalid data found; unable to read PNG file"),
Self::InvalidDepthForType(d, ref c) => {
write!(f, "Invalid bit depth {d} for color type {c}")
}
PngError::NotPNG => f.write_str("Invalid header detected; Not a PNG file"),
PngError::ReadFailed(ref s, ref e) => write!(f, "Failed to read from {s}: {e}"),
PngError::TruncatedData => {
f.write_str("Missing data in the file; the file is truncated")
}
PngError::WriteFailed(ref s, ref e) => write!(f, "Failed to write to {s}: {e}"),
PngError::Other(ref s) => f.write_str(s),
Self::NotPNG => f.write_str("Invalid header detected; Not a PNG file"),
Self::ReadFailed(ref s, ref e) => write!(f, "Failed to read from {s}: {e}"),
Self::TruncatedData => f.write_str("Missing data in the file; the file is truncated"),
Self::WriteFailed(ref s, ref e) => write!(f, "Failed to write to {s}: {e}"),
Self::Other(ref s) => f.write_str(s),
}
}
}
@ -65,7 +63,7 @@ impl fmt::Display for PngError {
impl PngError {
#[cold]
#[must_use]
pub fn new(description: &str) -> PngError {
PngError::Other(description.into())
pub fn new(description: &str) -> Self {
Self::Other(description.into())
}
}

View file

@ -127,22 +127,18 @@ impl RowFilter {
}
Self::Average => {
for (i, byte) in data.iter().enumerate() {
buf.push(match i.checked_sub(bpp) {
Some(x) => byte.wrapping_sub(
((u16::from(data[x]) + u16::from(prev_line[i])) >> 1) as u8,
),
None => byte.wrapping_sub(prev_line[i] >> 1),
});
buf.push(byte.wrapping_sub(i.checked_sub(bpp).map_or_else(
|| prev_line[i] >> 1,
|x| ((u16::from(data[x]) + u16::from(prev_line[i])) >> 1) as u8,
)));
}
}
Self::Paeth => {
for (i, byte) in data.iter().enumerate() {
buf.push(match i.checked_sub(bpp) {
Some(x) => {
byte.wrapping_sub(paeth_predictor(data[x], prev_line[i], prev_line[x]))
}
None => byte.wrapping_sub(prev_line[i]),
});
buf.push(byte.wrapping_sub(i.checked_sub(bpp).map_or_else(
|| prev_line[i],
|x| paeth_predictor(data[x], prev_line[i], prev_line[x]),
)));
}
}
}
@ -241,10 +237,7 @@ impl RowFilter {
Self::Sub => {
for (i, &cur) in data.iter().enumerate() {
let prev_byte = i.checked_sub(bpp).and_then(|x| buf.get(x).copied());
buf.push(match prev_byte {
Some(b) => cur.wrapping_add(b),
None => cur,
});
buf.push(prev_byte.map_or(cur, |b| cur.wrapping_add(b)));
}
}
Self::Up => {
@ -257,10 +250,10 @@ impl RowFilter {
Self::Average => {
for (i, (&cur, &last)) in data.iter().zip(prev_line).enumerate() {
let prev_byte = i.checked_sub(bpp).and_then(|x| buf.get(x).copied());
buf.push(match prev_byte {
Some(b) => cur.wrapping_add(((u16::from(b) + u16::from(last)) >> 1) as u8),
None => cur.wrapping_add(last >> 1),
});
buf.push(cur.wrapping_add(prev_byte.map_or_else(
|| last >> 1,
|b| ((u16::from(b) + u16::from(last)) >> 1) as u8,
)));
}
}
Self::Paeth => {

View file

@ -35,18 +35,16 @@ impl IhdrData {
/// Byte length of IDAT that is correct for this IHDR
#[must_use]
pub fn raw_data_size(&self) -> usize {
pub const fn raw_data_size(&self) -> usize {
let w = self.width as usize;
let h = self.height as usize;
let bpp = self.bpp();
fn bitmap_size(bpp: usize, w: usize, h: usize) -> usize {
const fn bitmap_size(bpp: usize, w: usize, h: usize) -> usize {
(w * bpp).div_ceil(8) * h
}
if !self.interlaced {
bitmap_size(bpp, w, h) + h
} else {
if self.interlaced {
let mut size = bitmap_size(bpp, (w + 7) >> 3, (h + 7) >> 3) + ((h + 7) >> 3);
if w > 4 {
size += bitmap_size(bpp, (w + 3) >> 3, (h + 7) >> 3) + ((h + 7) >> 3);
@ -60,6 +58,8 @@ impl IhdrData {
size += bitmap_size(bpp, w >> 1, (h + 1) >> 1) + ((h + 1) >> 1);
}
size + bitmap_size(bpp, w, h >> 1) + (h >> 1)
} else {
bitmap_size(bpp, w, h) + h
}
}
}

View file

@ -149,7 +149,7 @@ fn deinterlace_bytes(png: &PngImage) -> Vec<u8> {
lines.concat()
}
fn increment_pass(current_pass: &mut u8, ihdr: &IhdrData) -> bool {
const fn increment_pass(current_pass: &mut u8, ihdr: &IhdrData) -> bool {
if *current_pass == 7 {
return false;
}

View file

@ -28,7 +28,7 @@ mod rayon;
use std::{
fs::{File, Metadata},
io::{BufWriter, Read, Write, stdin, stdout},
path::Path,
path::{Path, PathBuf},
sync::{
Arc,
atomic::{AtomicBool, Ordering},
@ -275,8 +275,7 @@ pub fn optimize(input: &InFile, output: &OutFile, opts: &Options) -> PngResult<(
(OutFile::Path { path, .. }, _) => {
let output_path = path
.as_ref()
.map(|p| p.as_path())
.unwrap_or_else(|| input.path().unwrap());
.map_or_else(|| input.path().unwrap(), PathBuf::as_path);
let out_file = File::create(output_path)
.map_err(|err| PngError::WriteFailed(output_path.display().to_string(), err))?;
if let Some(metadata_input) = &opt_metadata_preserved {
@ -449,9 +448,9 @@ fn optimize_raw(
let (result, deflater) = if opts.idat_recoding || reduction_occurred {
let result = perform_trials(
new_image.clone(),
new_image,
opts,
deadline.clone(),
deadline,
max_size,
eval_result,
eval_filters,
@ -497,7 +496,7 @@ fn perform_trials(
if !filters.is_empty() {
trace!("Evaluating {} filters", filters.len());
let eval = Evaluator::new(
deadline.clone(),
deadline,
filters,
eval_deflater,
opts.optimize_alpha,
@ -529,7 +528,7 @@ fn perform_trials(
trace!(">{bytes} bytes");
}
Err(_) => (),
};
}
}
return Some(result);
}
@ -659,7 +658,7 @@ fn recompress_frames(
}
/// Check if an image was already optimized prior to oxipng's operations
fn is_fully_optimized(original_size: usize, optimized_size: usize, opts: &Options) -> bool {
const fn is_fully_optimized(original_size: usize, optimized_size: usize, opts: &Options) -> bool {
original_size <= optimized_size && !opts.force
}
@ -674,7 +673,7 @@ fn copy_permissions(metadata_input: &Metadata, out_file: &File) -> PngResult<()>
}
#[cfg(not(feature = "filetime"))]
fn copy_times(_: &Metadata, _: &Path) -> PngResult<()> {
const fn copy_times(_: &Metadata, _: &Path) -> PngResult<()> {
Ok(())
}

View file

@ -19,7 +19,12 @@ mod rayon;
#[cfg(feature = "zopfli")]
use std::num::NonZeroU64;
use std::{
ffi::OsString, fs::DirBuilder, io::Write, path::PathBuf, process::ExitCode, time::Duration,
ffi::{OsStr, OsString},
fs::DirBuilder,
io::Write,
path::PathBuf,
process::ExitCode,
time::Duration,
};
use clap::ArgMatches;
@ -127,7 +132,7 @@ fn collect_files(
warn!("{} is a directory, skipping", input.display());
}
continue;
};
}
let out_file =
if let (Some(out_dir), &OutFile::Path { preserve_attrs, .. }) = (out_dir, out_file) {
let path = Some(out_dir.join(input.file_name().unwrap()));
@ -143,7 +148,7 @@ fn collect_files(
} else {
// Skip non png files if not given on top level
if !top_level && {
let extension = input.extension().map(|f| f.to_ascii_lowercase());
let extension = input.extension().map(OsStr::to_ascii_lowercase);
extension != Some(OsString::from("png"))
&& extension != Some(OsString::from("apng"))
} {
@ -200,7 +205,7 @@ fn parse_opts_into_struct(
};
// Get custom brute settings and rebuild the filter set to apply them
let mut brute_lines = matches.get_one::<usize>("brute-lines").cloned();
let mut brute_lines = matches.get_one::<usize>("brute-lines").copied();
let mut brute_level = matches.get_one::<i64>("brute-level").map(|x| *x as u8);
let mut new_filters = IndexSet::new();
for mut f in opts.filters.drain(..) {
@ -243,7 +248,7 @@ fn parse_opts_into_struct(
match DirBuilder::new().recursive(true).create(path) {
Ok(()) => (),
Err(x) => return Err(format!("Could not create output directory {x}")),
};
}
} else if !path.is_dir() {
return Err(format!(
"{} is an existing file (not a directory), cannot create directory",

View file

@ -32,8 +32,8 @@ impl OutFile {
///
/// This is a convenience method for `OutFile::Path { path: Some(path), preserve_attrs: false }`.
#[must_use]
pub fn from_path(path: PathBuf) -> Self {
OutFile::Path {
pub const fn from_path(path: PathBuf) -> Self {
Self::Path {
path: Some(path),
preserve_attrs: false,
}
@ -199,7 +199,7 @@ impl Options {
self
}
fn apply_preset_2(self) -> Self {
const fn apply_preset_2(self) -> Self {
self
}

View file

@ -123,7 +123,7 @@ impl PngData {
});
}
b"acTL" => {
warn!("Stripping animation data from APNG - image will become standard PNG")
warn!("Stripping animation data from APNG - image will become standard PNG");
}
_ => (),
}
@ -133,9 +133,8 @@ impl PngData {
if idat_data.is_empty() {
return Err(PngError::ChunkMissing("IDAT"));
}
let ihdr_chunk = match key_chunks.remove(b"IHDR") {
Some(ihdr) => ihdr,
None => return Err(PngError::ChunkMissing("IHDR")),
let Some(ihdr_chunk) = key_chunks.remove(b"IHDR") else {
return Err(PngError::ChunkMissing("IHDR"));
};
let ihdr = parse_ihdr_chunk(
&ihdr_chunk,
@ -310,11 +309,10 @@ impl PngImage {
match &self.ihdr.color_type {
ColorType::Indexed { palette } => {
let plte = 12 + palette.len() * 3;
if let Some(trns) = palette.iter().rposition(|p| p.a != 255) {
plte + 12 + trns + 1
} else {
plte
}
palette
.iter()
.rposition(|p| p.a != 255)
.map_or(plte, |trns| plte + 12 + trns + 1)
}
ColorType::Grayscale { transparent_shade } if transparent_shade.is_some() => 12 + 2,
ColorType::RGB { transparent_color } if transparent_color.is_some() => 12 + 6,
@ -348,7 +346,7 @@ impl PngImage {
last_pass = line.pass;
}
last_line.resize(line.data.len(), 0);
let filter = RowFilter::try_from(line.filter).map_err(|_| PngError::InvalidData)?;
let filter = RowFilter::try_from(line.filter).map_err(|()| PngError::InvalidData)?;
filter.unfilter_line(bpp, line.data, &last_line, &mut unfiltered_buf);
unfiltered.extend_from_slice(&unfiltered_buf);
std::mem::swap(&mut last_line, &mut unfiltered_buf);

View file

@ -138,7 +138,7 @@ impl Iterator for ScanLineRanges {
pixels_per_line += 1;
}
_ => (),
};
}
let current_pass = Some(pass.0);
if pass.1 + y_steps >= self.height {
pass.0 += 1;

View file

@ -81,7 +81,7 @@ pub fn reduced_alpha_channel(png: &PngImage, optimize_alpha: bool) -> Option<Png
raw_data.resize(raw_data.len() + colored_bytes, trns);
}
_ => raw_data.extend_from_slice(&pixel[0..colored_bytes]),
};
}
}
// Construct the color type with appropriate transparency data

View file

@ -70,17 +70,17 @@ pub fn reduced_bit_depth_8_or_less(png: &PngImage) -> Option<PngImage> {
return None;
}
let mut minimum_bits = 1;
if let ColorType::Indexed { palette } = &png.ihdr.color_type {
let minimum_bits = if let ColorType::Indexed { palette } = &png.ihdr.color_type {
// We can easily determine minimum depth by the palette size
minimum_bits = match palette.len() {
match palette.len() {
0..=2 => 1,
3..=4 => 2,
5..=16 => 4,
_ => return None,
};
}
} else {
let mut minimum_bits = 1;
// Finding minimum depth for grayscale is much more complicated
let mut mask = 1;
let mut divisions = 1..8;
@ -110,7 +110,9 @@ pub fn reduced_bit_depth_8_or_less(png: &PngImage) -> Option<PngImage> {
break;
}
}
}
minimum_bits
};
let mut reduced = Vec::with_capacity(png.data.len());
let mask = (1 << minimum_bits) - 1;

View file

@ -54,10 +54,10 @@ pub fn reduced_to_indexed(png: &PngImage, allow_grayscale: bool) -> Option<PngIm
let transparency_pixel = transparent_shade.map(|t| Gray::from(t as u8));
pmap.into_iter()
.map(|px| {
RGB::from(px).with_alpha(if Some(px) != transparency_pixel {
255
} else {
RGB::from(px).with_alpha(if Some(px) == transparency_pixel {
0
} else {
255
})
})
.collect()
@ -68,10 +68,10 @@ pub fn reduced_to_indexed(png: &PngImage, allow_grayscale: bool) -> Option<PngIm
let transparency_pixel = transparent_color.map(|t| t.map(|c| c as u8));
pmap.into_iter()
.map(|px| {
px.with_alpha(if Some(px) != transparency_pixel {
255
} else {
px.with_alpha(if Some(px) == transparency_pixel {
0
} else {
255
})
})
.collect()

View file

@ -206,7 +206,7 @@ fn apply_palette_reorder(png: &PngImage, remapping: &[usize]) -> Option<PngImage
// Find the most popular color on the image edges (the pixels neighboring the filter bytes)
fn most_popular_edge_color(num_colors: usize, png: &PngImage) -> Option<usize> {
let mut counts = [0u32; 256];
let mut counts = [0_u32; 256];
for line in png.scan_lines(false) {
if let &[first, .., last] = line.data {
counts[first as usize] += 1;
@ -229,7 +229,7 @@ fn most_popular_edge_color(num_colors: usize, png: &PngImage) -> Option<usize> {
// Find the most popular color in the image, along with its count
fn most_popular_color(num_colors: usize, png: &PngImage) -> (usize, u32) {
let mut counts = [0u32; 256];
let mut counts = [0_u32; 256];
for &val in &png.data {
counts[val as usize] += 1;
}
@ -261,7 +261,7 @@ fn apply_most_popular_color(png: &PngImage, remapping: &mut [usize]) {
// Calculate co-occurences matrix
fn co_occurrence_matrix(num_colors: usize, png: &PngImage) -> Vec<Vec<u32>> {
let mut matrix = vec![vec![0u32; num_colors]; num_colors];
let mut matrix = vec![vec![0_u32; num_colors]; num_colors];
let mut prev: Option<ScanLine> = None;
let mut prev_val = None;
for line in png.scan_lines(false) {

View file

@ -44,7 +44,7 @@ fn load_png_image_from_memory(png_data: &[u8]) -> Result<Vec<RgbaImage>, image::
decoder
.apng()?
.into_frames()
.map(|f| f.map(|f| f.into_buffer()))
.map(|f| f.map(image::Frame::into_buffer))
.collect()
} else {
DynamicImage::from_decoder(decoder).map(|i| vec![i.into_rgba8()])

View file

@ -36,7 +36,7 @@ fn test_it_converts(
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
Err(x) => panic!("{}", x),
};
}
let output = output.path().unwrap();
assert!(output.exists());

View file

@ -38,7 +38,7 @@ fn test_it_converts(
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
Err(x) => panic!("{}", x),
};
}
let output = output.path().unwrap();
assert!(output.exists());

View file

@ -50,7 +50,7 @@ fn test_it_converts_callbacks<CBPRE, CBPOST>(
match oxipng::optimize(&InFile::Path(input), output, opts) {
Ok(_) => (),
Err(x) => panic!("{}", x),
};
}
let output = output.path().unwrap();
assert!(output.exists());
@ -445,7 +445,7 @@ fn preserve_attrs() {
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
Err(x) => panic!("{}", x),
};
}
let output = output.path().unwrap();
assert!(output.exists());

View file

@ -40,7 +40,7 @@ fn test_it_converts(
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
Err(x) => panic!("{}", x),
};
}
let output = output.path().unwrap();
assert!(output.exists());

View file

@ -36,7 +36,7 @@ fn test_it_converts(
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
Err(x) => panic!("{}", x),
};
}
let output = output.path().unwrap();
assert!(output.exists());

View file

@ -41,7 +41,7 @@ fn test_it_converts(
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
Err(x) => panic!("{}", x),
};
}
let output = output.path().unwrap();
assert!(output.exists());
@ -999,7 +999,7 @@ fn palette_should_be_reduced_with_dupes() {
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
Err(x) => panic!("{}", x),
};
}
let output = output.path().unwrap();
assert!(output.exists());
@ -1036,7 +1036,7 @@ fn palette_should_be_reduced_with_unused() {
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
Err(x) => panic!("{}", x),
};
}
let output = output.path().unwrap();
assert!(output.exists());
@ -1074,7 +1074,7 @@ fn palette_should_be_reduced_with_bkgd() {
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
Err(x) => panic!("{}", x),
};
}
let output = output.path().unwrap();
assert!(output.exists());
@ -1112,7 +1112,7 @@ fn palette_should_be_reduced_with_both() {
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
Err(x) => panic!("{}", x),
};
}
let output = output.path().unwrap();
assert!(output.exists());
@ -1149,7 +1149,7 @@ fn palette_should_be_reduced_with_missing() {
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
Err(x) => panic!("{}", x),
};
}
let output = output.path().unwrap();
assert!(output.exists());

View file

@ -42,7 +42,7 @@ fn test_it_converts(
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
Err(x) => panic!("{}", x),
};
}
let output = output.path().unwrap();
assert!(output.exists());

View file

@ -37,7 +37,7 @@ fn test_it_converts(
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
Err(x) => panic!("{}", x),
};
}
let output = output.path().unwrap();
assert!(output.exists());