use std::io::Write;
use rawzip::{CompressionMethod, DataDescriptorOutput, ZipArchive, ZipArchiveWriter};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let source = build_source()?;
let copied = copy_verbatim(&source)?;
verify(&source, &copied)?;
println!(
"passthrough copy verified: {} bytes -> {} bytes",
source.len(),
copied.len()
);
Ok(())
}
fn build_source() -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let mut out = std::io::Cursor::new(Vec::new());
let mut archive = ZipArchiveWriter::new(&mut out);
let (mut entry, config) = archive
.new_file("deflated.txt")
.compression_method(CompressionMethod::DEFLATE)
.start()?;
let encoder = flate2::write::DeflateEncoder::new(&mut entry, flate2::Compression::default());
let mut writer = config.wrap(encoder);
writer.write_all(
b"the quick brown fox jumps over the lazy dog\n"
.repeat(64)
.as_slice(),
)?;
let (encoder, desc) = writer.finish()?;
encoder.finish()?;
entry.finish(desc)?;
let (mut entry, config) = archive
.new_file("stored.bin")
.compression_method(CompressionMethod::STORE)
.start()?;
let mut writer = config.wrap(&mut entry);
writer.write_all(b"raw stored bytes")?;
let (_, desc) = writer.finish()?;
entry.finish(desc)?;
archive.finish()?;
Ok(out.into_inner())
}
fn copy_verbatim(source: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let archive = ZipArchive::from_slice(source)?;
let mut out = std::io::Cursor::new(Vec::new());
let mut writer = ZipArchiveWriter::new(&mut out);
let mut entries = archive.entries();
while let Some(dir) = entries.next_entry()? {
if dir.is_dir() {
continue;
}
let name = dir.file_path().try_normalize()?.as_ref().to_string();
let crc = dir.crc32();
let uncompressed_size = dir.uncompressed_size_hint();
let method = dir.compression_method();
let local = archive.get_entry(dir.wayfinder())?;
let raw = local.data();
let (mut entry, _config) = writer
.new_file(name.as_str())
.compression_method(method)
.start()?;
entry.write_all(raw)?;
entry.finish(DataDescriptorOutput::new(crc, uncompressed_size))?;
}
writer.finish()?;
Ok(out.into_inner())
}
type Decoded = Vec<(String, Vec<u8>)>;
fn verify(source: &[u8], copied: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
let a = decode_all(source)?;
let b = decode_all(copied)?;
assert_eq!(a, b, "copied archive content differs from source");
Ok(())
}
fn decode_all(bytes: &[u8]) -> Result<Decoded, Box<dyn std::error::Error>> {
let archive = ZipArchive::from_slice(bytes)?;
let mut entries = archive.entries();
let mut result = Vec::new();
while let Some(dir) = entries.next_entry()? {
if dir.is_dir() {
continue;
}
let name = dir.file_path().try_normalize()?.as_ref().to_string();
let entry = archive.get_entry(dir.wayfinder())?;
let decoder: Box<dyn std::io::Read> = match dir.compression_method() {
CompressionMethod::STORE => Box::new(entry.data()),
CompressionMethod::DEFLATE => {
Box::new(flate2::bufread::DeflateDecoder::new(entry.data()))
}
other => return Err(format!("unexpected method {other:?}").into()),
};
let mut verifier = entry.verifying_reader(decoder);
let mut content = Vec::new();
std::io::copy(&mut verifier, &mut content)?;
result.push((name, content));
}
Ok(result)
}