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
126 Ok(())
127 }
128
129 pub fn stream_file(
136 &mut self,
137 name: &str,
138 options: &FileOptions,
139 ) -> io::Result<FileStreamer<'_, W>> {
140 let writer = self.raw.start_stream_raw(&mut self.writer, name, options)?;
141
142 Ok(FileStreamer {
143 writer: Counter::new(Crc32Writer::new(Compressor::new(
144 writer,
145 options.compression_method,
146 options.level,
147 )?)),
148 })
149 }
150
151 pub fn add_directory(&mut self, name: &str) -> io::Result<()> {
153 self.raw.write_file_raw(
154 &mut self.writer,
155 name,
156 &[],
157 &raw::Metadata {
158 compression_method: CompressionMethod::STORE,
159 compressed_size: 0,
160 uncompressed_size: 0,
161 crc32: 0,
162 typ: crate::FileType::Directory,
163 modified_at: Timestamp::UNIX_EPOCH,
164 },
165 "",
166 )
167 }
168
169 pub fn add_symlink(&mut self, name: &str, target: &str) -> io::Result<()> {
175 self.raw.write_file_raw(
176 &mut self.writer,
177 name,
178 target.as_bytes(),
179 &raw::Metadata {
180 compression_method: CompressionMethod::STORE,
181 compressed_size: target.len() as _,
182 uncompressed_size: target.len() as _,
183 crc32: crc32fast::hash(target.as_bytes()),
184 typ: crate::FileType::Symlink,
185 modified_at: Timestamp::UNIX_EPOCH,
186 },
187 "",
188 )
189 }
190
191 #[inline]
199 pub fn recover(&mut self) -> io::Result<()>
200 where
201 W: io::Seek,
202 {
203 self.raw.recover(&mut self.writer)
204 }
205
206 #[inline]
208 pub fn get_ref(&self) -> &W {
209 &self.writer
210 }
211
212 #[inline]
216 pub fn get_mut(&mut self) -> &mut W {
217 &mut self.writer
218 }
219
220 #[inline]
222 pub fn flush(&mut self) -> io::Result<()> {
223 self.writer.flush()
224 }
225
226 #[inline]
231 pub fn finish(mut self) -> io::Result<W> {
232 self.raw.finish(&mut self.writer)?;
233 Ok(self.writer)
234 }
235}
236
237pub struct FileStreamer<'a, W: io::Write> {
243 writer: Counter<Crc32Writer<Compressor<raw::RawFileStreamer<'a, &'a mut W>>>>,
244}
245
246impl<W: io::Write> io::Write for FileStreamer<'_, W> {
247 #[inline]
248 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
249 self.writer.write(buf)
250 }
251
252 #[inline]
253 fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
254 self.writer.write_vectored(bufs)
255 }
256
257 #[inline]
258 fn flush(&mut self) -> io::Result<()> {
259 self.writer.flush()
260 }
261}
262
263impl<W: io::Write> FileStreamer<'_, W> {
264 pub fn finish(self) -> io::Result<()> {
266 self.finish_with_comment("")
267 }
268
269 pub fn finish_with_comment(self, comment: &str) -> io::Result<()> {
271 let uncompressed_size = self.writer.amt;
272 let crc32 = self.writer.inner.result();
273
274 let raw_writer = self.writer.inner.into_inner().finish()?;
275
276 raw_writer.finish(uncompressed_size, crc32, comment)
277 }
278}
279
280impl<'a, W: io::Write + fmt::Debug> fmt::Debug for FileStreamer<'a, W> {
281 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282 f.write_str("FileStreamer { .. }")
283 }
284}