use crate::archive::Archive;
use crate::detect::sniff;
use crate::error::{ArchiveError, Result};
use crate::peel::{peel_bytes, PeelOutcome};
use crate::resolve::Limits;
#[derive(Debug)]
pub enum Peel {
NotPacked,
Inner(Vec<u8>),
}
pub fn peel_archive(data: &[u8], name: Option<&str>, limits: &Limits) -> Result<Peel> {
let format = sniff(name, data);
if format.is_compression_wrapper() {
if !has_compression_ext(name) {
return Ok(Peel::NotPacked);
}
let inner = match peel_bytes(data, name)? {
PeelOutcome::Peeled { inner, .. } => inner,
PeelOutcome::NotPacked => return Ok(Peel::NotPacked),
};
return cap(inner, limits);
}
if format.is_archive() {
let Some(mut archive) = Archive::open(data, name)? else {
return Ok(Peel::NotPacked);
};
let file_indices: Vec<usize> = archive
.entries()
.iter()
.enumerate()
.filter(|(_, e)| !e.is_dir)
.map(|(i, _)| i)
.collect();
if let [only] = file_indices[..] {
let inner = archive.read(only)?;
return cap(inner, limits);
}
return Ok(Peel::NotPacked);
}
Ok(Peel::NotPacked)
}
fn cap(inner: Vec<u8>, limits: &Limits) -> Result<Peel> {
if inner.len() as u64 > limits.max_total_inflated {
return Err(ArchiveError::TotalInflatedExceeded {
cap: limits.max_total_inflated,
});
}
Ok(Peel::Inner(inner))
}
fn has_compression_ext(name: Option<&str>) -> bool {
let Some(name) = name else {
return false;
};
let lower = name.to_ascii_lowercase();
[
".gz", ".bz2", ".tgz", ".tar.gz", ".tbz2", ".tar.bz2", ".zip", ".clbx", ".7z",
]
.iter()
.any(|e| lower.ends_with(e))
}