rustrails-support 0.1.1

Core utilities (ActiveSupport equivalent)
Documentation
use flate2::Compression;
use flate2::read::GzDecoder;
use flate2::write::GzEncoder;
use std::io::{Read, Write};

/// Errors returned while decompressing gzip data.
#[derive(Debug, thiserror::Error)]
pub enum GzipError {
    /// The gzip stream could not be read.
    #[error("gzip I/O error: {0}")]
    Io(#[from] std::io::Error),
}

/// Compresses raw bytes with gzip.
#[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}"),
    }
}

/// Decompresses gzip bytes into the original raw data.
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:"));
    }
}