1use crate::Bundle;
7use std::{
8 fs::File,
9 io::{Cursor, Read, Seek},
10 path::Path,
11 str::FromStr,
12};
13use tectonic_errors::prelude::*;
14use tectonic_io_base::{digest, InputHandle, InputOrigin, IoProvider, OpenResult};
15use tectonic_status_base::{NoopStatusBackend, StatusBackend};
16use zip::{result::ZipError, ZipArchive};
17
18pub struct ZipBundle<R: Read + Seek> {
20 zip: ZipArchive<R>,
21}
22
23impl<R: Read + Seek> ZipBundle<R> {
24 pub fn new(reader: R) -> Result<ZipBundle<R>> {
26 Ok(ZipBundle {
27 zip: ZipArchive::new(reader)?,
28 })
29 }
30}
31
32impl ZipBundle<File> {
33 pub fn open<P: AsRef<Path>>(path: P) -> Result<ZipBundle<File>> {
35 Self::new(File::open(path)?)
36 }
37}
38
39impl<R: Read + Seek> IoProvider for ZipBundle<R> {
40 fn input_open_name(
41 &mut self,
42 name: &str,
43 _status: &mut dyn StatusBackend,
44 ) -> OpenResult<InputHandle> {
45 let mut zipitem = match self.zip.by_name(name) {
50 Ok(f) => f,
51 Err(e) => {
52 return match e {
53 ZipError::Io(sube) => OpenResult::Err(sube.into()),
54 ZipError::FileNotFound => OpenResult::NotAvailable,
55 _ => OpenResult::Err(e.into()),
56 };
57 }
58 };
59
60 let s = zipitem.size();
61 if s >= u32::MAX as u64 {
62 return OpenResult::Err(anyhow!("Zip item too large."));
63 }
64 let mut buf = Vec::with_capacity(s as usize);
65
66 if let Err(e) = zipitem.read_to_end(&mut buf) {
67 return OpenResult::Err(e.into());
68 }
69
70 OpenResult::Ok(InputHandle::new_read_only(
71 name,
72 Cursor::new(buf),
73 InputOrigin::Other,
74 ))
75 }
76}
77
78impl<R: Read + Seek> Bundle for ZipBundle<R> {
79 fn all_files(&self) -> Vec<String> {
80 self.zip.file_names().map(|x| x.to_owned()).collect()
81 }
82
83 fn get_digest(&mut self) -> Result<tectonic_io_base::digest::DigestData> {
84 let digest_text = match self.input_open_name(digest::DIGEST_NAME, &mut NoopStatusBackend {})
85 {
86 OpenResult::Ok(h) => {
87 let mut text = String::new();
88 h.take(64).read_to_string(&mut text)?;
89 text
90 }
91
92 OpenResult::NotAvailable => {
93 bail!("bundle does not provide needed SHA256SUM file");
94 }
95
96 OpenResult::Err(e) => {
97 return Err(e);
98 }
99 };
100
101 Ok(atry!(digest::DigestData::from_str(&digest_text); ["corrupted SHA256 digest data"]))
102 }
103}