alpm_compress/decompression/
decoder.rs1use std::{
4 fmt::Debug,
5 fs::File,
6 io::{BufReader, Read},
7};
8
9use bzip2::bufread::BzDecoder;
10use flate2::bufread::GzDecoder;
11use liblzma::bufread::XzDecoder;
12use zstd::Decoder;
13
14use crate::{Error, decompression::DecompressionSettings};
15
16pub enum CompressionDecoder<'a> {
21 Bzip2(BzDecoder<BufReader<File>>),
23
24 Gzip(GzDecoder<BufReader<File>>),
26
27 Xz(XzDecoder<BufReader<File>>),
29
30 Zstd(Decoder<'a, BufReader<File>>),
32
33 None(BufReader<File>),
35}
36
37impl CompressionDecoder<'_> {
38 pub fn new(file: File, settings: DecompressionSettings) -> Result<Self, Error> {
48 match settings {
49 DecompressionSettings::Bzip2 => Ok(Self::Bzip2(BzDecoder::new(BufReader::new(file)))),
50 DecompressionSettings::Gzip => Ok(Self::Gzip(GzDecoder::new(BufReader::new(file)))),
51 DecompressionSettings::Xz => Ok(Self::Xz(XzDecoder::new(BufReader::new(file)))),
52 DecompressionSettings::Zstd => Ok(Self::Zstd(
53 Decoder::new(file).map_err(Error::CreateZstandardDecoder)?,
54 )),
55 DecompressionSettings::None => Ok(Self::None(BufReader::new(file))),
56 }
57 }
58}
59
60impl Debug for CompressionDecoder<'_> {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 write!(
63 f,
64 "CompressionDecoder({})",
65 match self {
66 CompressionDecoder::Bzip2(_) => "Bzip2",
67 CompressionDecoder::Gzip(_) => "Gzip",
68 CompressionDecoder::Xz(_) => "Xz",
69 CompressionDecoder::Zstd(_) => "Zstd",
70 CompressionDecoder::None(_) => "None",
71 }
72 )
73 }
74}
75
76impl Read for CompressionDecoder<'_> {
77 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
78 match self {
79 CompressionDecoder::Bzip2(decoder) => decoder.read(buf),
80 CompressionDecoder::Gzip(decoder) => decoder.read(buf),
81 CompressionDecoder::Xz(decoder) => decoder.read(buf),
82 CompressionDecoder::Zstd(decoder) => decoder.read(buf),
83 CompressionDecoder::None(reader) => reader.read(buf),
84 }
85 }
86
87 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
88 match self {
89 CompressionDecoder::Bzip2(decoder) => decoder.read_to_end(buf),
90 CompressionDecoder::Gzip(decoder) => decoder.read_to_end(buf),
91 CompressionDecoder::Xz(decoder) => decoder.read_to_end(buf),
92 CompressionDecoder::Zstd(decoder) => decoder.read_to_end(buf),
93 CompressionDecoder::None(reader) => reader.read_to_end(buf),
94 }
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use std::io::{Seek, Write};
101
102 use rstest::rstest;
103 use tempfile::tempfile;
104 use testresult::TestResult;
105
106 use super::*;
107 use crate::compression::{
108 Bzip2CompressionLevel,
109 CompressionEncoder,
110 CompressionSettings,
111 GzipCompressionLevel,
112 XzCompressionLevel,
113 ZstdCompressionLevel,
114 ZstdThreads,
115 };
116
117 #[rstest]
120 #[case::bzip2(DecompressionSettings::Bzip2, CompressionSettings::Bzip2 {
121 compression_level: Bzip2CompressionLevel::default()
122 })]
123 #[case::gzip(DecompressionSettings::Gzip, CompressionSettings::Gzip {
124 compression_level: GzipCompressionLevel::default()
125 })]
126 #[case::xz(DecompressionSettings::Xz, CompressionSettings::Xz {
127 compression_level: XzCompressionLevel::default()
128 })]
129 #[case::zstd(DecompressionSettings::Zstd, CompressionSettings::Zstd {
130 compression_level: ZstdCompressionLevel::default(),
131 threads: ZstdThreads::new(0),
132 })]
133 #[case::no_compression(DecompressionSettings::None, CompressionSettings::None)]
134 fn test_compression_decoder_roundtrip(
135 #[case] decompression_settings: DecompressionSettings,
136 #[case] compression_settings: CompressionSettings,
137 ) -> TestResult {
138 let input_data = b"alpm4ever";
140
141 let mut file = tempfile()?;
143 {
144 let mut encoder = CompressionEncoder::new(file.try_clone()?, &compression_settings)?;
145 encoder.write_all(input_data)?;
146 encoder.flush()?;
147 encoder.finish()?;
148 }
149
150 file.rewind()?;
152
153 let mut decoder = CompressionDecoder::new(file, decompression_settings)?;
155 let mut output = Vec::new();
156 decoder.read_to_end(&mut output)?;
157
158 assert_eq!(output, input_data);
160 Ok(())
161 }
162
163 #[rstest]
164 #[case::bzip2(DecompressionSettings::Bzip2)]
165 #[case::gzip(DecompressionSettings::Gzip)]
166 #[case::xz(DecompressionSettings::Xz)]
167 #[case::zstd(DecompressionSettings::Zstd)]
168 #[case::no_compression(DecompressionSettings::None)]
169 fn test_compression_decoder_debug(#[case] settings: DecompressionSettings) -> TestResult {
170 let file = tempfile()?;
171 let decoder = CompressionDecoder::new(file, settings)?;
172 let debug_str = format!("{decoder:?}");
173 assert!(debug_str.contains("CompressionDecoder"));
174 Ok(())
175 }
176}