1use crate::{
4 CompressionMethod, Timestamp,
5 compression::Compressor,
6 utils::{Counter, Crc32Writer},
7};
8use std::{fmt, io};
9
10mod raw;
11
12#[derive(Debug, Default, Clone)]
16#[non_exhaustive]
17pub struct FileOptions {
18 pub compression_method: CompressionMethod,
20 pub level: Option<i32>,
22 pub modified_at: Timestamp,
27}
28
29#[derive(Debug, Default)]
62pub struct ArchiveWriter<W: io::Write> {
63 writer: W,
64 raw: raw::RawArchiveWriter,
65}
66
67impl ArchiveWriter<std::fs::File> {
68 pub fn create(path: impl AsRef<std::path::Path>) -> io::Result<Self> {
73 std::fs::File::create(path).map(Self::new)
74 }
75
76 pub fn create_new(path: impl AsRef<std::path::Path>) -> io::Result<Self> {
79 std::fs::File::create_new(path).map(Self::new)
80 }
81}
82
83impl<W: io::Write> ArchiveWriter<W> {
84 #[inline]
86 pub fn new(writer: W) -> Self {
87 ArchiveWriter {
88 writer,
89 raw: raw::RawArchiveWriter::default(),
90 }
91 }
92
93 pub fn add_file<R: io::Read>(
97 &mut self,
98 name: &str,
99 mut content: R,
100 options: &FileOptions,
101 ) -> io::Result<()> {
102 let mut w = Crc32Writer::new(Compressor::new(
103 Vec::new(),
104 options.compression_method,
105 options.level,
106 )?);
107 let uncompressed_size = io::copy(&mut content, &mut w)?;
108 let crc32 = w.result();
109 let compressed = w.into_inner().finish()?;
110
111 self.raw.write_file_raw(
112 &mut self.writer,
113 name,
114 &compressed,
115 &raw::Metadata {
116 compression_method: options.compression_method,
117 compressed_size: compressed.len() as _,
118 uncompressed_size,
119 crc32,
120 typ: crate::FileType::File,
121 modified_at: options.modified_at,
122 },
123 )?;
124
125 Ok(())
126 }
127
128 pub fn stream_file(
135 &mut self,
136 name: &str,
137 options: &FileOptions,
138 ) -> io::Result<FileStreamer<'_, W>> {
139 let writer = self.raw.start_stream_raw(&mut self.writer, name, options)?;
140
141 Ok(FileStreamer {
142 writer: Counter::new(Crc32Writer::new(Compressor::new(
143 writer,
144 options.compression_method,
145 options.level,
146 )?)),
147 })
148 }
149
150 pub fn add_directory(&mut self, name: &str) -> io::Result<()> {
152 if !name.ends_with('/') {
153 return Err(io::Error::new(
154 io::ErrorKind::InvalidInput,
155 "directory name must end with '/'",
156 ));
157 }
158
159 self.raw.write_file_raw(
160 &mut self.writer,
161 name,
162 &[],
163 &raw::Metadata {
164 compression_method: CompressionMethod::STORE,
165 compressed_size: 0,
166 uncompressed_size: 0,
167 crc32: 0,
168 typ: crate::FileType::Directory,
169 modified_at: Timestamp::UNIX_EPOCH,
170 },
171 )
172 }
173
174 pub fn add_symlink(&mut self, name: &str, target: &str) -> io::Result<()> {
180 self.raw.write_file_raw(
181 &mut self.writer,
182 name,
183 target.as_bytes(),
184 &raw::Metadata {
185 compression_method: CompressionMethod::STORE,
186 compressed_size: target.len() as _,
187 uncompressed_size: target.len() as _,
188 crc32: crc32fast::hash(target.as_bytes()),
189 typ: crate::FileType::Symlink,
190 modified_at: Timestamp::UNIX_EPOCH,
191 },
192 )
193 }
194
195 #[inline]
203 pub fn recover(&mut self) -> io::Result<()>
204 where
205 W: io::Seek,
206 {
207 self.raw.recover(&mut self.writer)
208 }
209
210 #[inline]
212 pub fn get_ref(&self) -> &W {
213 &self.writer
214 }
215
216 #[inline]
220 pub fn get_mut(&mut self) -> &mut W {
221 &mut self.writer
222 }
223
224 #[inline]
226 pub fn flush(&mut self) -> io::Result<()> {
227 self.writer.flush()
228 }
229
230 #[inline]
235 pub fn finish(mut self) -> io::Result<W> {
236 self.raw.finish(&mut self.writer)?;
237 Ok(self.writer)
238 }
239}
240
241pub struct FileStreamer<'a, W: io::Write> {
247 writer: Counter<Crc32Writer<Compressor<raw::RawFileStreamer<'a, &'a mut W>>>>,
248}
249
250impl<W: io::Write> io::Write for FileStreamer<'_, W> {
251 #[inline]
252 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
253 self.writer.write(buf)
254 }
255
256 #[inline]
257 fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
258 self.writer.write_vectored(bufs)
259 }
260
261 #[inline]
262 fn flush(&mut self) -> io::Result<()> {
263 self.writer.flush()
264 }
265}
266
267impl<W: io::Write> FileStreamer<'_, W> {
268 pub fn finish(self) -> io::Result<()> {
270 let uncompressed_size = self.writer.amt;
271 let crc32 = self.writer.inner.result();
272
273 let raw_writer = self.writer.inner.into_inner().finish()?;
274
275 raw_writer.finish(uncompressed_size, crc32)
276 }
277}
278
279impl<'a, W: io::Write + fmt::Debug> fmt::Debug for FileStreamer<'a, W> {
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 f.write_str("FileStreamer { .. }")
282 }
283}