rs_zips2items2asn2zip/
lib.rs

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}