libmedusa_zip/
destination.rs1use displaydoc::Display;
13use parking_lot::Mutex;
14use thiserror::Error;
15use tokio::{
16 fs,
17 io::{self, AsyncSeekExt},
18 task,
19};
20use zip::{result::ZipError, ZipWriter};
21
22use std::{ops::DerefMut, path::Path, sync::Arc};
23
24#[derive(Debug, Display, Error)]
25pub enum DestinationError {
26 Io(#[from] io::Error),
28 Zip(#[from] ZipError),
30 Join(#[from] task::JoinError),
32}
33
34#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
35pub enum DestinationBehavior {
36 #[default]
38 AlwaysTruncate,
39 AppendOrFail,
41 OptimisticallyAppend,
43 AppendToNonZip,
48}
49
50impl DestinationBehavior {
51 pub async fn initialize(self, path: &Path) -> Result<ZipWriter<std::fs::File>, DestinationError> {
52 let (file, with_append) = match self {
53 Self::AlwaysTruncate => {
54 let f = fs::OpenOptions::new()
55 .write(true)
56 .create(true)
57 .truncate(true)
58 .open(path)
59 .await?;
60 (f, false)
61 },
62 Self::AppendOrFail => {
63 let f = fs::OpenOptions::new()
64 .write(true)
65 .read(true)
66 .open(path)
67 .await?;
68 (f, true)
69 },
70 Self::OptimisticallyAppend => {
71 match fs::OpenOptions::new()
72 .write(true)
73 .create_new(true)
74 .open(path)
75 .await
76 {
77 Ok(f) => (f, false),
78 Err(e) => match e.kind() {
79 io::ErrorKind::AlreadyExists => {
80 let f = fs::OpenOptions::new()
81 .write(true)
82 .read(true)
83 .open(path)
84 .await?;
85 (f, true)
86 },
87 _ => {
88 return Err(e.into());
89 },
90 },
91 }
92 },
93 Self::AppendToNonZip => {
94 let mut f = fs::OpenOptions::new()
95 .write(true)
96 .read(true)
97 .open(path)
98 .await?;
99 f.seek(io::SeekFrom::End(0)).await?;
106 (f, false)
107 },
108 };
109 let file = file.into_std().await;
110
111 let writer = task::spawn_blocking(move || {
112 if with_append {
113 Ok::<_, DestinationError>(ZipWriter::new_append(file)?)
114 } else {
115 Ok(ZipWriter::new(file))
116 }
117 })
118 .await??;
119
120 Ok(writer)
121 }
122}
123
124pub struct OutputWrapper<O> {
125 handle: Arc<Mutex<O>>,
126}
127
128impl<O> Clone for OutputWrapper<O> {
129 fn clone(&self) -> Self {
130 Self {
131 handle: Arc::clone(&self.handle),
132 }
133 }
134}
135
136impl<O> OutputWrapper<O> {
137 pub fn wrap(writer: O) -> Self {
138 Self {
139 handle: Arc::new(Mutex::new(writer)),
140 }
141 }
142
143 pub fn reclaim(self) -> O {
144 Arc::into_inner(self.handle)
145 .expect("expected this to be the last strong ref")
146 .into_inner()
147 }
148
149 pub fn lease(&self) -> impl DerefMut<Target=O>+'_ { self.handle.lock() }
150}