1use std::io;
2
3use io::BufRead;
4use io::Read;
5
6use io::Seek;
7
8use io::Write;
9
10use std::fs::File;
11
12use der::asn1::OctetString;
13
14use zip::ZipArchive;
15
16use zip::read::ZipFile;
17
18use zip::write::SimpleFileOptions;
19use zip::write::ZipWriter;
20
21pub use der;
22pub use zip;
23
24#[derive(Debug, PartialEq, Eq, Clone, Copy, der::Enumerated)]
25#[repr(u8)]
26pub enum CompressionMethod {
27 Unspecified = 0,
28 Store = 100,
29 Deflate = 108,
30}
31
32impl Default for CompressionMethod {
33 fn default() -> Self {
34 Self::Unspecified
35 }
36}
37
38#[derive(Default, der::Sequence)]
39pub struct ZipMeta {
40 pub filename: String,
41 pub comment: String,
42 pub modified_unixtime: u32,
43 pub compression: CompressionMethod,
44 pub is_dir: bool,
45}
46
47pub fn encode2buf<E>(e: &E, buf: &mut Vec<u8>) -> Result<(), io::Error>
48where
49 E: der::Encode,
50{
51 e.encode_to_vec(buf).map(|_| ()).map_err(io::Error::other)
52}
53
54#[derive(der::Sequence)]
55pub struct ZipItem {
56 pub meta: ZipMeta,
57 pub data: OctetString,
58}
59
60impl ZipItem {
61 pub fn to_buf(&self, buf: &mut Vec<u8>) -> Result<(), io::Error> {
62 encode2buf(self, buf)
63 }
64
65 pub fn items2buf(items: &Vec<Self>, buf: &mut Vec<u8>) -> Result<(), io::Error> {
66 encode2buf(items, buf)
67 }
68
69 pub fn into_mapd<M>(self, mapper: &mut M) -> Result<Self, io::Error>
70 where
71 M: FnMut(Self) -> Result<Self, io::Error>,
72 {
73 mapper(self)
74 }
75}
76
77pub struct RawZipEntry<'a, R>
78where
79 R: Read,
80{
81 pub zfile: ZipFile<'a, R>,
82}
83
84impl<'a, R> RawZipEntry<'a, R>
85where
86 R: Read,
87{
88 pub fn comment(&self) -> &str {
89 self.zfile.comment()
90 }
91
92 pub fn name(&self) -> &str {
93 self.zfile.name()
94 }
95
96 pub fn method(&self) -> CompressionMethod {
97 match self.zfile.compression() {
98 zip::CompressionMethod::Stored => CompressionMethod::Store,
99 zip::CompressionMethod::Deflated => CompressionMethod::Deflate,
100 _ => CompressionMethod::Unspecified,
101 }
102 }
103
104 pub fn unixtime(&self) -> Option<u32> {
105 let efields = self.zfile.extra_data_fields();
106 let mut filtered = efields.filter_map(|efield| match efield {
107 zip::extra_fields::ExtraField::ExtendedTimestamp(e) => e.mod_time(),
108 _ => None,
109 });
110 filtered.next()
111 }
112
113 pub fn is_dir(&self) -> bool {
114 self.zfile.is_dir()
115 }
116
117 pub fn to_meta(&self) -> ZipMeta {
118 ZipMeta {
119 filename: self.name().into(),
120 comment: self.comment().into(),
121 modified_unixtime: self.unixtime().unwrap_or_default(),
122 compression: self.method(),
123 is_dir: self.is_dir(),
124 }
125 }
126}
127
128impl<'a, R> RawZipEntry<'a, R>
129where
130 R: Read,
131{
132 pub fn to_buf(&mut self, buf: &mut Vec<u8>) -> Result<(), io::Error> {
133 buf.clear();
134 let zfile: &mut ZipFile<_> = &mut self.zfile;
135 io::copy(zfile, buf)?;
136 Ok(())
137 }
138}
139
140pub fn nop_mapper(original: ZipItem) -> Result<ZipItem, io::Error> {
141 Ok(original)
142}
143
144pub fn zip2items2iter<R>(mut z: ZipArchive<R>) -> impl Iterator<Item = Result<ZipItem, io::Error>>
145where
146 R: Read + Seek,
147{
148 let sz: usize = z.len();
149
150 let mut ix: usize = 0;
151
152 std::iter::from_fn(move || {
153 let ok: bool = ix < sz;
154 if !ok {
155 return None;
156 }
157
158 let rzfile: Result<ZipFile<_>, _> = z.by_index(ix);
159 ix += 1;
160
161 let rent: Result<RawZipEntry<_>, _> = rzfile
162 .map(|zfile| RawZipEntry { zfile })
163 .map_err(io::Error::other);
164
165 Some(rent.and_then(|mut rze| {
166 let meta: ZipMeta = rze.to_meta();
167 let mut buf: Vec<u8> = vec![];
168 rze.to_buf(&mut buf)?;
169
170 let zitem: ZipItem = ZipItem {
171 meta,
172 data: OctetString::new(buf).map_err(io::Error::other)?,
173 };
174 Ok(zitem)
175 }))
176 })
177}
178
179pub fn zipfile2items(
180 zfile: File,
181) -> Result<impl Iterator<Item = Result<ZipItem, io::Error>>, io::Error> {
182 let za: ZipArchive<_> = ZipArchive::new(zfile).map_err(io::Error::other)?;
183 Ok(zip2items2iter(za))
184}
185
186#[derive(Default)]
187pub struct ZipSequence {
188 pub zipname: String,
189 pub derdata: Vec<u8>,
190}
191
192pub fn zipfilename2sequence<C, N>(
193 zfilename: &str,
194 mut item_converter: &mut C,
195 name_converter: &N,
196 buf: &mut ZipSequence,
197) -> Result<(), io::Error>
198where
199 C: FnMut(ZipItem) -> Result<ZipItem, io::Error>,
200 N: Fn(&str, &mut String),
201{
202 let f: File = File::open(zfilename)?;
203 let items = zipfile2items(f)?;
204 let mapd = items.map(|ritem| ritem.and_then(&mut item_converter));
205 buf.zipname.clear();
206 name_converter(zfilename, &mut buf.zipname);
207 let ritems: Result<Vec<_>, _> = mapd.collect();
208 let items: Vec<ZipItem> = ritems?;
209 buf.derdata.clear();
210 ZipItem::items2buf(&items, &mut buf.derdata)?;
211 Ok(())
212}
213
214pub fn str2string(s: &str, d: &mut String) {
215 d.push_str(s)
216}
217
218pub fn nop_name_converter(original: &str, tgt: &mut String) {
219 tgt.clear();
220 str2string(original, tgt)
221}
222
223pub fn zipnames2sequence2zipwtr<I, C, N, W>(
224 filenames: I,
225 mut item_converter: C,
226 name_converter: N,
227 mut zwtr: ZipWriter<W>,
228 opts: SimpleFileOptions,
229) -> Result<(), io::Error>
230where
231 I: Iterator<Item = String>,
232 C: FnMut(ZipItem) -> Result<ZipItem, io::Error>,
233 N: Fn(&str, &mut String),
234 W: Write + Seek,
235{
236 let mut buf: ZipSequence = ZipSequence::default();
237 for zname in filenames {
238 zipfilename2sequence(&zname, &mut item_converter, &name_converter, &mut buf)?;
239 let zname: &str = &buf.zipname;
240 let der: &[u8] = &buf.derdata;
241 zwtr.start_file(zname, opts)?;
242 zwtr.write_all(der)?;
243 }
244 let mut w: W = zwtr.finish()?;
245 w.flush()
246}
247
248pub fn zipnames2sequence2zipfile<I, C, N>(
249 filenames: I,
250 item_converter: C,
251 name_converter: N,
252 opts: SimpleFileOptions,
253 outfile: File,
254) -> Result<(), io::Error>
255where
256 I: Iterator<Item = String>,
257 C: FnMut(ZipItem) -> Result<ZipItem, io::Error>,
258 N: Fn(&str, &mut String),
259{
260 let zwtr: ZipWriter<_> = ZipWriter::new(outfile);
261 zipnames2sequence2zipwtr(filenames, item_converter, name_converter, zwtr, opts)
262}
263
264pub fn zipnames2sequence2zipfile_default<I>(filenames: I, outfile: File) -> Result<(), io::Error>
265where
266 I: Iterator<Item = String>,
267{
268 zipnames2sequence2zipfile(
269 filenames,
270 nop_mapper,
271 nop_name_converter,
272 SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored),
273 outfile,
274 )
275}
276
277pub fn stdin2names() -> impl Iterator<Item = String> {
278 let i = io::stdin();
279 let il = i.lock();
280 let names = il.lines();
281 names.map_while(Result::ok)
282}
283
284pub fn stdin2names2sequence2zipfile<C, N>(
285 item_converter: C,
286 name_converter: N,
287 opts: SimpleFileOptions,
288 outfile: File,
289) -> Result<(), io::Error>
290where
291 C: FnMut(ZipItem) -> Result<ZipItem, io::Error>,
292 N: Fn(&str, &mut String),
293{
294 zipnames2sequence2zipfile(stdin2names(), item_converter, name_converter, opts, outfile)
295}
296
297pub fn stdin2names2sequence2zipfile_default(outfile: File) -> Result<(), io::Error> {
298 let noerr = stdin2names();
299 zipnames2sequence2zipfile_default(noerr, outfile)
300}