sos_archive/
writer.rs

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
10/// Write to an archive.
11pub struct Writer<W: AsyncWrite + Unpin> {
12    writer: ZipFileWriter<W>,
13}
14
15impl<W: AsyncWrite + Unpin> Writer<W> {
16    /// Create a new writer with a manifest.
17    pub fn new(inner: W) -> Self {
18        Self {
19            writer: ZipFileWriter::with_tokio(inner),
20        }
21    }
22
23    /// Add a file to the archive.
24    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    /// Finish building the archive.
62    pub async fn finish(self) -> Result<Compat<W>> {
63        Ok(self.writer.close().await?)
64    }
65}