use crate::utils::get_env_with_prefix;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
#[derive(Default)]
pub enum CompressionAlgorithm {
Gzip,
Brotli,
#[default]
Both,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CompressionConfig {
#[serde(default = "default_enabled")]
pub enabled: bool,
#[serde(default)]
pub algorithm: CompressionAlgorithm,
#[serde(default = "default_compression_level")]
pub level: u8,
#[serde(default = "default_brotli_quality")]
pub brotli_quality: u8,
}
impl Default for CompressionConfig {
fn default() -> Self {
Self {
enabled: default_enabled(),
algorithm: CompressionAlgorithm::default(),
level: default_compression_level(),
brotli_quality: default_brotli_quality(),
}
}
}
impl CompressionConfig {
pub fn builder() -> CompressionConfigBuilder {
CompressionConfigBuilder::new()
}
pub fn from_env() -> Self {
let mut config = Self::default();
if let Some(enabled) = get_env_with_prefix("COMPRESSION_ENABLED") {
match enabled.parse() {
Ok(value) => config.enabled = value,
Err(_) => {
tracing::warn!(
value = %enabled,
"Invalid COMPRESSION_ENABLED (expected true/false), using default ({})",
default_enabled()
);
}
}
}
if let Some(algorithm) = get_env_with_prefix("COMPRESSION_ALGORITHM") {
config.algorithm = match algorithm.to_lowercase().as_str() {
"gzip" => CompressionAlgorithm::Gzip,
"brotli" => CompressionAlgorithm::Brotli,
"both" => CompressionAlgorithm::Both,
other => {
tracing::warn!(
value = %other,
"Invalid COMPRESSION_ALGORITHM (expected gzip/brotli/both), using default"
);
CompressionAlgorithm::default()
}
};
}
if let Some(level) = get_env_with_prefix("COMPRESSION_LEVEL") {
match level.parse::<u8>() {
Ok(l) => config.level = l.clamp(1, 9),
Err(_) => tracing::warn!(
value = %level,
"Invalid COMPRESSION_LEVEL (expected 1-9), using default ({})",
default_compression_level()
),
}
}
if let Some(quality) = get_env_with_prefix("COMPRESSION_BROTLI_QUALITY") {
match quality.parse::<u8>() {
Ok(q) => config.brotli_quality = q.clamp(1, 11),
Err(_) => tracing::warn!(
value = %quality,
"Invalid COMPRESSION_BROTLI_QUALITY (expected 1-11), using default ({})",
default_brotli_quality()
),
}
}
config
}
}
#[must_use = "builder does nothing until you call build()"]
pub struct CompressionConfigBuilder {
config: CompressionConfig,
}
impl CompressionConfigBuilder {
pub fn new() -> Self {
Self {
config: CompressionConfig::default(),
}
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.config.enabled = enabled;
self
}
pub fn algorithm(mut self, algorithm: CompressionAlgorithm) -> Self {
self.config.algorithm = algorithm;
self
}
pub fn gzip(mut self) -> Self {
self.config.algorithm = CompressionAlgorithm::Gzip;
self
}
pub fn brotli(mut self) -> Self {
self.config.algorithm = CompressionAlgorithm::Brotli;
self
}
pub fn both(mut self) -> Self {
self.config.algorithm = CompressionAlgorithm::Both;
self
}
pub fn level(mut self, level: u8) -> Self {
self.config.level = level.clamp(1, 9);
self
}
pub fn brotli_quality(mut self, quality: u8) -> Self {
self.config.brotli_quality = quality.clamp(1, 11);
self
}
pub fn build(self) -> CompressionConfig {
self.config
}
}
impl Default for CompressionConfigBuilder {
fn default() -> Self {
Self::new()
}
}
fn default_enabled() -> bool {
true
}
fn default_compression_level() -> u8 {
6 }
fn default_brotli_quality() -> u8 {
4 }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = CompressionConfig::default();
assert!(config.enabled);
assert_eq!(config.algorithm, CompressionAlgorithm::Both);
assert_eq!(config.level, 6);
assert_eq!(config.brotli_quality, 4);
}
#[test]
fn test_builder() {
let config = CompressionConfig::builder().gzip().level(9).build();
assert_eq!(config.algorithm, CompressionAlgorithm::Gzip);
assert_eq!(config.level, 9);
}
#[test]
fn test_level_clamping() {
let config = CompressionConfig::builder()
.level(15) .build();
assert_eq!(config.level, 9);
let config = CompressionConfig::builder()
.level(0) .build();
assert_eq!(config.level, 1);
}
#[test]
fn test_brotli_quality_clamping() {
let config = CompressionConfig::builder()
.brotli_quality(15) .build();
assert_eq!(config.brotli_quality, 11);
let config = CompressionConfig::builder()
.brotli_quality(0) .build();
assert_eq!(config.brotli_quality, 1);
}
}