use flate2::Compression;
use flate2::read::GzDecoder;
use flate2::write::GzEncoder;
use std::io::{Read, Write};
#[derive(Debug, thiserror::Error)]
pub enum GzipError {
#[error("gzip I/O error: {0}")]
Io(#[from] std::io::Error),
}
#[must_use]
pub fn compress(data: &[u8]) -> Vec<u8> {
let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
if let Err(error) = encoder.write_all(data) {
unreachable!("writing gzip data into Vec<u8> should not fail: {error}");
}
match encoder.finish() {
Ok(bytes) => bytes,
Err(error) => unreachable!("finishing gzip data into Vec<u8> should not fail: {error}"),
}
}
pub fn decompress(data: &[u8]) -> Result<Vec<u8>, GzipError> {
let mut decoder = GzDecoder::new(data);
let mut output = Vec::new();
decoder.read_to_end(&mut output)?;
Ok(output)
}
#[cfg(test)]
mod tests {
use super::{compress, decompress};
#[test]
fn gzip_round_trips_non_empty_payloads() {
let input = b"RustRails supports gzip";
let compressed = compress(input);
let decompressed = decompress(&compressed).expect("gzip data should decompress");
assert_eq!(decompressed, input);
}
#[test]
fn gzip_round_trips_empty_payloads() {
let compressed = compress(b"");
let decompressed = decompress(&compressed).expect("gzip data should decompress");
assert!(decompressed.is_empty());
}
#[test]
fn compressed_output_differs_from_raw_input_for_non_empty_data() {
let input = b"aaaaabbbbbcccccdddddeeeee";
let compressed = compress(input);
assert_ne!(compressed, input);
}
#[test]
fn invalid_gzip_data_returns_an_error() {
let error = decompress(b"not-gzip").expect_err("invalid gzip data should fail");
assert!(error.to_string().starts_with("gzip I/O error:"));
}
}