tectonic_bundles/
ttb_fs.rs1use crate::{
9 ttb::{TTBFileIndex, TTBFileInfo, TTBv1Header},
10 Bundle, FileIndex, FileInfo,
11};
12use flate2::read::GzDecoder;
13use std::{
14 convert::TryFrom,
15 fs::File,
16 io::{Cursor, Read, Seek, SeekFrom},
17 path::Path,
18};
19use tectonic_errors::prelude::*;
20use tectonic_io_base::{digest::DigestData, InputHandle, InputOrigin, IoProvider, OpenResult};
21use tectonic_status_base::StatusBackend;
22
23fn read_fileinfo<'a>(fileinfo: &TTBFileInfo, reader: &'a mut File) -> Result<Box<dyn Read + 'a>> {
26 reader.seek(SeekFrom::Start(fileinfo.start))?;
27 Ok(Box::new(GzDecoder::new(
28 reader.take(fileinfo.gzip_len as u64),
29 )))
30}
31
32pub struct TTBFsBundle<T>
34where
35 for<'a> T: FileIndex<'a>,
36{
37 file: File,
38 index: T,
39}
40
41impl TTBFsBundle<TTBFileIndex> {
43 pub fn new(file: File) -> Result<Self> {
45 Ok(TTBFsBundle {
46 file,
47 index: TTBFileIndex::default(),
48 })
49 }
50
51 fn get_header(&mut self) -> Result<TTBv1Header> {
52 self.file.seek(SeekFrom::Start(0))?;
53 let mut header: [u8; 70] = [0u8; 70];
54 self.file.read_exact(&mut header)?;
55 self.file.seek(SeekFrom::Start(0))?;
56 let header = TTBv1Header::try_from(header)?;
57 Ok(header)
58 }
59
60 fn fill_index(&mut self) -> Result<()> {
62 let header = self.get_header()?;
63 let info = TTBFileInfo {
64 start: header.index_start,
65 gzip_len: header.index_real_len,
66 real_len: header.index_gzip_len,
67 path: "/INDEX".to_owned(),
68 name: "INDEX".to_owned(),
69 hash: None,
70 };
71
72 let mut reader = read_fileinfo(&info, &mut self.file)?;
73 self.index.initialize(&mut reader)?;
74
75 Ok(())
76 }
77
78 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
80 Self::new(File::open(path)?)
81 }
82}
83
84impl IoProvider for TTBFsBundle<TTBFileIndex> {
85 fn input_open_name(
86 &mut self,
87 name: &str,
88 _status: &mut dyn StatusBackend,
89 ) -> OpenResult<InputHandle> {
90 if self.index.is_empty() {
92 if let Err(e) = self.fill_index() {
93 return OpenResult::Err(e);
94 }
95 }
96
97 let info = match self.index.search(name) {
98 None => return OpenResult::NotAvailable,
99 Some(s) => s,
100 };
101
102 let mut v: Vec<u8> = Vec::with_capacity(info.real_len as usize);
103
104 match read_fileinfo(&info, &mut self.file) {
105 Err(e) => return OpenResult::Err(e),
106 Ok(mut b) => {
107 if let Err(e) = b.read_to_end(&mut v) {
108 return OpenResult::Err(e.into());
109 }
110 }
111 };
112
113 OpenResult::Ok(InputHandle::new_read_only(
114 name,
115 Cursor::new(v),
116 InputOrigin::Other,
117 ))
118 }
119}
120
121impl Bundle for TTBFsBundle<TTBFileIndex> {
122 fn all_files(&self) -> Vec<String> {
123 self.index.iter().map(|x| x.path().to_owned()).collect()
124 }
125
126 fn get_digest(&mut self) -> Result<DigestData> {
127 let header = self.get_header()?;
128 Ok(header.digest)
129 }
130}