Skip to main content

rustrails_support/
gzip.rs

1use flate2::Compression;
2use flate2::read::GzDecoder;
3use flate2::write::GzEncoder;
4use std::io::{Read, Write};
5
6/// Errors returned while decompressing gzip data.
7#[derive(Debug, thiserror::Error)]
8pub enum GzipError {
9    /// The gzip stream could not be read.
10    #[error("gzip I/O error: {0}")]
11    Io(#[from] std::io::Error),
12}
13
14/// Compresses raw bytes with gzip.
15#[must_use]
16pub fn compress(data: &[u8]) -> Vec<u8> {
17    let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
18    if let Err(error) = encoder.write_all(data) {
19        unreachable!("writing gzip data into Vec<u8> should not fail: {error}");
20    }
21    match encoder.finish() {
22        Ok(bytes) => bytes,
23        Err(error) => unreachable!("finishing gzip data into Vec<u8> should not fail: {error}"),
24    }
25}
26
27/// Decompresses gzip bytes into the original raw data.
28pub fn decompress(data: &[u8]) -> Result<Vec<u8>, GzipError> {
29    let mut decoder = GzDecoder::new(data);
30    let mut output = Vec::new();
31    decoder.read_to_end(&mut output)?;
32    Ok(output)
33}
34
35#[cfg(test)]
36mod tests {
37    use super::{compress, decompress};
38
39    #[test]
40    fn gzip_round_trips_non_empty_payloads() {
41        let input = b"RustRails supports gzip";
42        let compressed = compress(input);
43        let decompressed = decompress(&compressed).expect("gzip data should decompress");
44
45        assert_eq!(decompressed, input);
46    }
47
48    #[test]
49    fn gzip_round_trips_empty_payloads() {
50        let compressed = compress(b"");
51        let decompressed = decompress(&compressed).expect("gzip data should decompress");
52
53        assert!(decompressed.is_empty());
54    }
55
56    #[test]
57    fn compressed_output_differs_from_raw_input_for_non_empty_data() {
58        let input = b"aaaaabbbbbcccccdddddeeeee";
59        let compressed = compress(input);
60
61        assert_ne!(compressed, input);
62    }
63
64    #[test]
65    fn invalid_gzip_data_returns_an_error() {
66        let error = decompress(b"not-gzip").expect_err("invalid gzip data should fail");
67        assert!(error.to_string().starts_with("gzip I/O error:"));
68    }
69}