1use crate::Result;
2use async_zip::{
3 tokio::write::ZipFileWriter, Compression, ZipDateTimeBuilder,
4 ZipEntryBuilder,
5};
6use time::OffsetDateTime;
7use tokio::io::AsyncWrite;
8use tokio_util::compat::Compat;
9
10pub struct Writer<W: AsyncWrite + Unpin> {
12 writer: ZipFileWriter<W>,
13}
14
15impl<W: AsyncWrite + Unpin> Writer<W> {
16 pub fn new(inner: W) -> Self {
18 Self {
19 writer: ZipFileWriter::with_tokio(inner),
20 }
21 }
22
23 pub async fn add_file(
25 &mut self,
26 path: &str,
27 content: &[u8],
28 ) -> Result<()> {
29 tracing::debug!(
30 path= %path,
31 len = %content.len(),
32 "create_archive::add_file"
33 );
34 self.append_file_buffer(path, content).await
35 }
36
37 async fn append_file_buffer(
38 &mut self,
39 path: &str,
40 buffer: &[u8],
41 ) -> Result<()> {
42 let now = OffsetDateTime::now_utc();
43 let (hours, minutes, seconds) = now.time().as_hms();
44 let month: u8 = now.month().into();
45
46 let dt = ZipDateTimeBuilder::new()
47 .year(now.year())
48 .month(month.into())
49 .day(now.day().into())
50 .hour(hours.into())
51 .minute(minutes.into())
52 .second(seconds.into())
53 .build();
54
55 let entry = ZipEntryBuilder::new(path.into(), Compression::Deflate)
56 .last_modification_date(dt);
57 self.writer.write_entry_whole(entry, buffer).await?;
58 Ok(())
59 }
60
61 pub async fn finish(self) -> Result<Compat<W>> {
63 Ok(self.writer.close().await?)
64 }
65}