rustrails_support/
gzip.rs1use flate2::Compression;
2use flate2::read::GzDecoder;
3use flate2::write::GzEncoder;
4use std::io::{Read, Write};
5
6#[derive(Debug, thiserror::Error)]
8pub enum GzipError {
9 #[error("gzip I/O error: {0}")]
11 Io(#[from] std::io::Error),
12}
13
14#[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
27pub 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}