From 8d37d2f9ccf0be356b933232987a5f8e61ec9cc0 Mon Sep 17 00:00:00 2001 From: Ryan Fowler Date: Thu, 28 Aug 2025 18:22:54 -0700 Subject: [PATCH] Use reasonable default quality levels --- README.md | 16 +++++++++--- src/encode.rs | 65 +++++++++++++++++++++++++++++++++++++--------- src/main.rs | 34 ++++++++++++++++-------- src/precompress.rs | 19 +++++++++++--- 4 files changed, 103 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index c296a0b..10875d8 100644 --- a/README.md +++ b/README.md @@ -54,16 +54,24 @@ Options: -V, --version Print version ``` -By default, all compression algorithms are enabled. To specify the specific -types of compression you want to enable, you can use the `-c` flag with the -values: +By default, the `brotli`, `gzip`, and `zstd` algorithms are enabled. To specify +the specific types of compression you want to enable, you can use the `-c` flag +with the values: - `br` or `brotli` - `de` or `deflate` - `gz` or `gzip` - `zst` or `zstd` -By default, the highest supported compression quality is used for each algorithm. +The default compression qualities used per algorithm are: + +``` +brotli: 10 +deflate: 7 +gzip: 7 +zstd: 19 +``` + To specify a different quality, add the value after a colon like so: ``` diff --git a/src/encode.rs b/src/encode.rs index e2ae720..f67df39 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -11,21 +11,62 @@ use flate2::{ }; use zstd::Encoder; +use crate::precompress::Algorithm; + #[derive(Debug, Clone, Copy)] pub(crate) struct Quality { - pub(crate) brotli: u8, - pub(crate) deflate: u8, - pub(crate) gzip: u8, - pub(crate) zstd: u8, + pub(crate) brotli: i8, + pub(crate) deflate: i8, + pub(crate) gzip: i8, + pub(crate) zstd: i8, } impl Default for Quality { fn default() -> Self { Quality { - brotli: 11, - deflate: 9, - gzip: 9, - zstd: 21, + brotli: 10, + deflate: 7, + gzip: 7, + zstd: 19, + } + } +} + +impl Quality { + pub(crate) fn set(&mut self, algorithm: Algorithm, quality: i8) -> bool { + match algorithm { + Algorithm::Brotli => { + if (0..=11).contains(&quality) { + self.brotli = quality; + true + } else { + false + } + } + Algorithm::Deflate => { + if (1..=9).contains(&quality) { + self.deflate = quality; + true + } else { + false + } + } + Algorithm::Gzip => { + if (1..=9).contains(&quality) { + self.gzip = quality; + true + } else { + false + } + } + Algorithm::Zstd => { + if (-7..=22).contains(&quality) { + self.zstd = quality; + true + } else { + false + } + } } } } @@ -45,10 +86,10 @@ impl Context { Context { read_buf: vec![0; buf_size], write_buf: vec![0; buf_size], - brotli_quality: i32::from(quality.brotli), - deflate_quality: u32::from(quality.deflate), - gzip_quality: u32::from(quality.gzip), - zstd_quality: i32::from(quality.zstd), + brotli_quality: quality.brotli as i32, + deflate_quality: quality.deflate as u32, + gzip_quality: quality.gzip as u32, + zstd_quality: quality.zstd as i32, } } diff --git a/src/main.rs b/src/main.rs index 985a17c..1e827e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -86,10 +86,10 @@ fn parse_compression(compression: Option>) -> (Algorithms, Quality) let raw = v .into_iter() .flat_map(|s| s.split(',').map(|s| s.to_owned()).collect::>()); - let mut algs = Algorithms::default(); + let mut algs = Algorithms::empty(); for s in raw { let (c, q) = if let Some((c, q)) = s.split_once(':') { - let q: u8 = match q.parse() { + let q: i8 = match q.parse() { Ok(q) => q, Err(_) => { eprintln!("Error: invalid compression quality: {q}"); @@ -104,26 +104,38 @@ fn parse_compression(compression: Option>) -> (Algorithms, Quality) match c { "br" | "brotli" => { algs.brotli = true; - if let Some(q) = q { - quality.brotli = q; + if let Some(q) = q + && !quality.set(Algorithm::Brotli, q) + { + eprintln!("Error: invalid brotli compression quality: {q}"); + exit(1); } } "de" | "deflate" => { algs.deflate = true; - if let Some(q) = q { - quality.deflate = q; + if let Some(q) = q + && !quality.set(Algorithm::Deflate, q) + { + eprintln!("Error: invalid deflate compression quality: {q}"); + exit(1); } } "gz" | "gzip" => { algs.gzip = true; - if let Some(q) = q { - quality.gzip = q; + if let Some(q) = q + && !quality.set(Algorithm::Gzip, q) + { + eprintln!("Error: invalid gzip compression quality: {q}"); + exit(1); } } "zst" | "zstd" => { algs.zstd = true; - if let Some(q) = q { - quality.zstd = q; + if let Some(q) = q + && !quality.set(Algorithm::Zstd, q) + { + eprintln!("Error: invalid zstd compression quality: {q}"); + exit(1); } } _ => { @@ -134,7 +146,7 @@ fn parse_compression(compression: Option>) -> (Algorithms, Quality) } algs }) - .unwrap_or_else(Algorithms::all_enabled); + .unwrap_or_default(); (algs, quality) } diff --git a/src/precompress.rs b/src/precompress.rs index a1255d2..925c53a 100644 --- a/src/precompress.rs +++ b/src/precompress.rs @@ -45,7 +45,7 @@ impl Algorithm { } } -#[derive(Debug, Default, Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub(crate) struct Algorithms { pub(crate) brotli: bool, pub(crate) deflate: bool, @@ -53,15 +53,26 @@ pub(crate) struct Algorithms { pub(crate) zstd: bool, } -impl Algorithms { - pub(crate) fn all_enabled() -> Self { +impl Default for Algorithms { + fn default() -> Self { Self { brotli: true, - deflate: true, + deflate: false, gzip: true, zstd: true, } } +} + +impl Algorithms { + pub(crate) fn empty() -> Self { + Self { + brotli: false, + deflate: false, + gzip: false, + zstd: false, + } + } pub(crate) fn iter(self) -> impl Iterator { Algorithm::iter().filter(move |algorithm| self.is_enabled(*algorithm))