use crate::AnyReader;
use peekable::Peekable;
use std::fmt::{Debug, Formatter};
use std::io;
use std::io::Read;
use tracing::trace;
pub struct AnyFormat<T: Read> {
pub kind: FormatKind,
reader: Peekable<AnyReader<T>>,
}
impl<T: Read> AnyFormat<T> {
pub fn from_reader(reader: T) -> io::Result<AnyFormat<T>> {
const MAX_PEEK_BUFFER_SIZE: usize = 262;
let compression_reader = AnyReader::from_reader(reader)?;
let format: FormatKind = (&compression_reader).into();
let mut reader = Peekable::with_capacity(compression_reader, MAX_PEEK_BUFFER_SIZE);
reader.fill_peek_buf()?;
let buf = crate::peek_upto::<MAX_PEEK_BUFFER_SIZE>(&mut reader)?;
trace!("peeked {} bytes", buf.len());
let format: FormatKind = if infer::archive::is_tar(buf) {
FormatKind::Tar
} else if infer::archive::is_zip(buf) {
FormatKind::Zip
} else {
format
};
trace!("format detected: {format:?}");
Ok(AnyFormat {
kind: format,
reader,
})
}
pub fn get_ref(&self) -> &T {
self.reader.get_ref().1.get_ref()
}
}
impl<T: Read> Debug for AnyFormat<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AnyFormat")
.field("kind", &self.kind)
.finish()
}
}
impl<T: Read> Read for AnyFormat<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.reader.read(buf)
}
}
#[derive(
Debug,
Copy,
Clone,
Eq,
PartialEq,
Hash,
Default,
strum::EnumString,
strum::Display,
strum::EnumIs,
)]
#[strum(serialize_all = "lowercase", ascii_case_insensitive)]
pub enum FormatKind {
Gzip,
Zstd,
Bzip2,
Xz,
Zip,
Tar,
#[default]
Unknown,
}
impl<T: Read> From<&AnyReader<T>> for FormatKind {
fn from(reader: &AnyReader<T>) -> Self {
match reader {
AnyReader::Gzip(_) => FormatKind::Gzip,
AnyReader::Zst(_) => FormatKind::Zstd,
AnyReader::Bzip2(_) => FormatKind::Bzip2,
AnyReader::Xz(_) => FormatKind::Xz,
AnyReader::Unknown(_) => FormatKind::Unknown,
}
}
}
impl<T: Read> From<AnyReader<T>> for FormatKind {
fn from(reader: AnyReader<T>) -> Self {
(&reader).into()
}
}