use crate::{
File,
error::{ArchiveError, Result},
traits::Decode,
};
use flate2::read::GzDecoder;
use std::io::{BufReader, Read};
use super::Gz;
struct GzHeader {
filename: Option<String>,
mtime: Option<u64>,
}
fn parse_header(bytes: &[u8]) -> Option<GzHeader> {
if bytes.len() < 10 || bytes[0] != 0x1f || bytes[1] != 0x8b {
return None;
}
let flg = bytes[3];
let mtime_raw = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
let mtime = if mtime_raw == 0 {
None
} else {
Some(mtime_raw as u64)
};
let mut idx = 10;
if flg & 0x04 != 0 {
if idx + 2 > bytes.len() {
return None;
}
let xlen = u16::from_le_bytes([bytes[idx], bytes[idx + 1]]) as usize;
idx += 2 + xlen;
}
let mut filename = None;
if flg & 0x08 != 0 && idx < bytes.len() {
let start = idx;
while idx < bytes.len() && bytes[idx] != 0 {
idx += 1;
}
let name = String::from_utf8_lossy(&bytes[start..idx]).to_string();
if !name.is_empty() {
filename = Some(name);
}
}
Some(GzHeader { filename, mtime })
}
impl Decode for Gz {
fn decode<T: AsRef<[u8]>>(buffer: T) -> Result<Vec<File>> {
let buffer = buffer.as_ref();
let header = parse_header(buffer);
let decoder = GzDecoder::new(buffer);
let mut buf_reader = BufReader::new(decoder);
let mut decompressed = Vec::new();
buf_reader.read_to_end(&mut decompressed).map_err(|e| {
ArchiveError::DecompressionError(format!("GZ decompression failed: {}", e))
})?;
let path = header
.as_ref()
.and_then(|h| h.filename.clone())
.unwrap_or_default();
Ok(vec![File {
path,
buffer: decompressed,
mode: None,
is_dir: false,
last_modified: header.as_ref().and_then(|h| h.mtime),
}])
}
}