1use crate::AnyReader;
2use peekable::Peekable;
3use std::fmt::{Debug, Formatter};
4use std::io;
5use std::io::Read;
6use tracing::trace;
7
8pub struct AnyFormat<T: Read> {
38 pub kind: FormatKind,
39 reader: Peekable<AnyReader<T>>,
40}
41
42impl<T: Read> AnyFormat<T> {
43 pub fn from_reader(reader: T) -> io::Result<AnyFormat<T>> {
44 const MAX_PEEK_BUFFER_SIZE: usize = 262;
45
46 let compression_reader = AnyReader::from_reader(reader)?;
47 let format: FormatKind = (&compression_reader).into();
48 trace!(format=%format, "initial format kind detected, attempting refinement");
49 let mut reader = Peekable::with_capacity(compression_reader, MAX_PEEK_BUFFER_SIZE);
50 reader.fill_peek_buf().ok();
51 let buf = crate::peek_upto::<MAX_PEEK_BUFFER_SIZE>(&mut reader);
52 trace!("peeked {} bytes", buf.len());
53
54 let format: FormatKind = if infer::archive::is_tar(buf) {
55 FormatKind::Tar
56 } else if infer::archive::is_zip(buf) {
57 FormatKind::Zip
58 } else if infer::app::is_coff(buf)
59 || infer::app::is_elf(buf)
60 || infer::app::is_mach(buf)
61 || infer::app::is_dex(buf)
62 || infer::app::is_llvm(buf)
63 || infer::app::is_java(buf)
64 || infer::app::is_elf(buf)
65 || infer::app::is_dll(buf)
66 || infer::app::is_exe(buf)
67 || infer::app::is_wasm(buf)
68 {
69 FormatKind::Executable
70 } else {
71 format
72 };
73
74 trace!("format detected: {format:?}");
75
76 Ok(AnyFormat {
77 kind: format,
78 reader,
79 })
80 }
81
82 pub fn get_ref(&self) -> &T {
83 self.reader.get_ref().1.get_ref()
84 }
85}
86
87impl<T: Read> Debug for AnyFormat<T> {
88 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
89 f.debug_struct("AnyFormat")
90 .field("kind", &self.kind)
91 .finish()
92 }
93}
94
95impl<T: Read> Read for AnyFormat<T> {
96 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
97 self.reader.read(buf)
98 }
99}
100
101#[derive(
103 Debug,
104 Copy,
105 Clone,
106 Eq,
107 PartialEq,
108 Hash,
109 Default,
110 strum::EnumString,
111 strum::Display,
112 strum::EnumIs,
113)]
114#[strum(serialize_all = "lowercase", ascii_case_insensitive)]
115pub enum FormatKind {
116 Gzip,
118 Zstd,
120 Bzip2,
122 Xz,
124 Zip,
126 Tar,
129 Executable,
131 #[default]
134 Unknown,
135}
136
137impl<T: Read> From<&AnyReader<T>> for FormatKind {
138 fn from(reader: &AnyReader<T>) -> Self {
140 match reader {
141 AnyReader::Gzip(_) => FormatKind::Gzip,
142 AnyReader::Zst(_) => FormatKind::Zstd,
143 AnyReader::Bzip2(_) => FormatKind::Bzip2,
144 AnyReader::Xz(_) => FormatKind::Xz,
145 AnyReader::Unknown(_) => FormatKind::Unknown,
146 }
147 }
148}
149
150impl<T: Read> From<AnyReader<T>> for FormatKind {
151 fn from(reader: AnyReader<T>) -> Self {
152 (&reader).into()
153 }
154}