1#[cfg(feature = "async")]
11use std::future::Future;
12use std::pin::Pin;
13#[cfg(feature = "async")]
14use std::task::Context;
15#[cfg(feature = "async")]
16use std::task::Poll;
17
18#[cfg(feature = "async")]
19use pin_project::pin_project;
20
21#[cfg(feature = "async")]
22use crate::error::Error;
23use crate::error::VfsResult;
24use crate::prelude::*;
25#[cfg(feature = "async")]
26use crate::traits::async_vfs::WriteSupportingVfsAsync;
27use crate::traits::vfs;
28use crate::traits::vfs::WriteSupportingVfs;
29
30pub struct AtomicDir<T>(pub T);
43
44impl<'vfs, T, Vfs: vfs::Vfs<'vfs> + 'vfs> ReadFrom<'vfs, Vfs> for AtomicDir<T>
45where
46 T: ReadFrom<'vfs, Vfs>,
47{
48 fn read_from(path: &Vfs::Path, vfs: Pin<&'vfs Vfs>) -> VfsResult<Self, Vfs> {
49 T::read_from(path, vfs).map(Self)
50 }
51}
52
53#[cfg(feature = "async")]
54impl<'vfs, T, Vfs: VfsAsync + 'vfs> ReadFromAsync<'vfs, Vfs> for AtomicDir<T>
55where
56 T: ReadFromAsync<'vfs, Vfs> + Send + 'static,
57{
58 type Future = Pin<Box<dyn Future<Output = VfsResult<Self, Vfs>> + Send + 'vfs>>;
59
60 fn read_from_async(
61 path: <Vfs::Path as PathType>::OwnedPath,
62 vfs: Pin<&'vfs Vfs>,
63 ) -> Self::Future {
64 let fut = T::read_from_async(path, vfs);
65 Box::pin(async move { fut.await.map(Self) })
66 }
67}
68
69#[must_use]
71pub trait TempDirApi<'vfs> {
72 type Vfs: WriteSupportingVfs<'vfs>;
74 fn path(&self) -> &<Self::Vfs as VfsCore>::Path;
76
77 fn persist_at(
79 self,
80 vfs: Pin<&'vfs Self::Vfs>,
81 path: &<Self::Vfs as VfsCore>::Path,
82 ) -> VfsResult<(), Self::Vfs>;
83
84 fn delete(self, vfs: Pin<&'vfs Self::Vfs>) -> VfsResult<(), Self::Vfs>;
86}
87
88pub trait VfsSupportsTemporaryDirectories<'vfs>: WriteSupportingVfs<'vfs> {
90 type TemporaryDirectory: TempDirApi<'vfs, Vfs = Self>;
92 fn create_temporary_directory(
94 self: Pin<&'vfs Self>,
95 ) -> VfsResult<Self::TemporaryDirectory, Self>;
96}
97
98impl<'vfs, T, Vfs: vfs::Vfs<'vfs> + vfs::WriteSupportingVfs<'vfs> + 'vfs> WriteTo<'vfs, Vfs>
99 for AtomicDir<T>
100where
101 T: WriteTo<'vfs, Vfs>,
102 Vfs: VfsSupportsTemporaryDirectories<'vfs>,
103{
104 fn write_to(&self, path: &Vfs::Path, vfs: Pin<&'vfs Vfs>) -> VfsResult<(), Vfs> {
105 let temp_dir = vfs.create_temporary_directory()?;
106 match self.0.write_to(temp_dir.path(), vfs) {
107 Ok(()) => temp_dir.persist_at(vfs, path),
108 Err(e) => {
109 let _ = temp_dir.delete(vfs); Err(e)
111 }
112 }
113 }
114}
115
116#[cfg(feature = "async")]
118#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
119pub trait TempDirApiAsync<'vfs> {
120 type Vfs: VfsAsync + WriteSupportingVfsAsync;
122 type FuturePersistAt: Future<Output = VfsResult<(), Self::Vfs>> + Send + 'vfs;
124 type FutureDelete: Future<Output = VfsResult<(), Self::Vfs>> + Send + 'vfs;
126
127 fn path(&self) -> &<Self::Vfs as VfsCore>::Path;
129
130 fn persist_at(
132 self,
133 vfs: Pin<&'vfs Self::Vfs>,
134 path: <<Self::Vfs as VfsCore>::Path as PathType>::OwnedPath,
135 ) -> Self::FuturePersistAt;
136
137 fn delete(self, vfs: Pin<&'vfs Self::Vfs>) -> Self::FutureDelete;
139}
140
141#[cfg(feature = "async")]
143#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
144pub trait VfsSupportsTemporaryDirectoriesAsync<'vfs>: VfsAsync + WriteSupportingVfsAsync {
145 type TemporaryDirectory: TempDirApiAsync<'vfs, Vfs = Self> + Send;
147 type TemporaryDirectoryFuture: Future<Output = VfsResult<Self::TemporaryDirectory, Self>>
149 + Send
150 + 'vfs;
151 fn create_temporary_directory(self: Pin<&'vfs Self>) -> Self::TemporaryDirectoryFuture;
153}
154
155#[cfg(feature = "async")]
156#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
157impl<'vfs, T, Vfs: WriteSupportingVfsAsync + 'vfs> WriteToAsync<'vfs, Vfs> for AtomicDir<T>
158where
159 T: WriteToAsync<'vfs, Vfs> + Send + 'static,
160 Vfs: VfsSupportsTemporaryDirectoriesAsync<'vfs> + VfsAsync + Send,
161{
162 type Future = Pin<Box<dyn Future<Output = VfsResult<(), Vfs>> + Send + 'vfs>>;
163
164 fn write_to_async(
165 self,
166 path: <Vfs::Path as PathType>::OwnedPath,
167 vfs: Pin<&'vfs Vfs>,
168 ) -> Self::Future {
169 Box::pin(async move {
170 let temp_dir = vfs.create_temporary_directory().await?;
171 match self.0.write_to_async(temp_dir.path().owned(), vfs).await {
172 Ok(()) => temp_dir.persist_at(vfs, path).await,
173 Err(e) => {
174 let fut = temp_dir.delete(vfs); let _ = fut.await;
176 Err(e)
177 }
178 }
179 })
180 }
181}
182
183#[cfg(feature = "async")]
184#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
185#[pin_project(project_replace = AtomicDirWriteToAsyncRefFutureProj)]
186#[doc(hidden)]
187pub enum AtomicDirWriteToAsyncRefFuture<'r, 'vfs, T, Vfs>
188where
189 Vfs: WriteSupportingVfsAsync + 'vfs,
190 T: WriteToAsyncRef<'vfs, Vfs> + Sync + 'static,
191 <T as WriteToAsyncRef<'vfs, Vfs>>::Future<'r>:
192 Future<Output = VfsResult<(), Vfs>> + Unpin + Send + 'r,
193 Vfs: VfsSupportsTemporaryDirectoriesAsync<'r> + VfsAsync + Send + 'static,
194 'vfs: 'r,
195{
196 CreatingTempDir(
197 Pin<
198 Box<
199 dyn Future<
200 Output = VfsResult<
201 <Vfs as VfsSupportsTemporaryDirectoriesAsync<'r>>::TemporaryDirectory,
202 Vfs,
203 >,
204 > + Send
205 + 'r,
206 >,
207 >,
208 &'r T,
209 <Vfs::Path as PathType>::OwnedPath,
210 Pin<&'r Vfs>,
211 ),
212 WritingToTempDir(
213 <Vfs as VfsSupportsTemporaryDirectoriesAsync<'r>>::TemporaryDirectory,
214 <T as WriteToAsyncRef<'vfs, Vfs>>::Future<'r>,
215 <Vfs::Path as PathType>::OwnedPath,
216 Pin<&'r Vfs>,
217 ),
218 PersistingTempDir(Pin<Box<dyn Future<Output = VfsResult<(), Vfs>> + Send + 'r>>),
219 DeletingTempDir(
220 Pin<Box<dyn Future<Output = VfsResult<(), Vfs>> + Send + 'r>>,
221 Error<<Vfs::Path as PathType>::OwnedPath>,
222 ),
223 Done,
224 Poison,
225}
226
227#[cfg(feature = "async")]
228#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
229impl<'r, 'vfs, T, Vfs> Future for AtomicDirWriteToAsyncRefFuture<'r, 'vfs, T, Vfs>
230where
231 Vfs: WriteSupportingVfsAsync + 'vfs,
232 T: WriteToAsyncRef<'vfs, Vfs> + Sync + 'static,
233 <T as WriteToAsyncRef<'vfs, Vfs>>::Future<'r>:
234 Future<Output = VfsResult<(), Vfs>> + Unpin + Send + 'r,
235 Vfs: VfsSupportsTemporaryDirectoriesAsync<'r> + VfsAsync + Send + 'static,
236 'vfs: 'r,
237{
238 type Output = VfsResult<(), Vfs>;
239
240 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
241 let this = self.as_mut().project_replace(Self::Poison);
242 match this {
243 AtomicDirWriteToAsyncRefFutureProj::CreatingTempDir(mut fut, val, path, vfs) => {
244 match fut.as_mut().poll(cx) {
245 Poll::Ready(Ok(temp_dir)) => {
246 let write_fut = val.write_to_async_ref(temp_dir.path().owned(), vfs);
247 self.as_mut().project_replace(Self::WritingToTempDir(
248 temp_dir, write_fut, path, vfs,
249 ));
250 cx.waker().wake_by_ref();
251 Poll::Pending
252 }
253 Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
254 Poll::Pending => {
255 self.as_mut()
256 .project_replace(Self::CreatingTempDir(fut, val, path, vfs));
257 Poll::Pending
258 }
259 }
260 }
261 AtomicDirWriteToAsyncRefFutureProj::WritingToTempDir(temp_dir, mut fut, path, vfs) => {
262 match Pin::new(&mut fut).poll(cx) {
263 Poll::Ready(Ok(())) => {
264 let persist_fut = temp_dir.persist_at(vfs.clone(), path.clone());
265 self.as_mut()
266 .project_replace(Self::PersistingTempDir(Box::pin(persist_fut)));
267 cx.waker().wake_by_ref();
268 Poll::Pending
269 }
270 Poll::Ready(Err(e)) => {
271 let delete_fut = temp_dir.delete(vfs.clone());
272 self.as_mut()
273 .project_replace(Self::DeletingTempDir(Box::pin(delete_fut), e));
274 cx.waker().wake_by_ref();
275 Poll::Pending
276 }
277 Poll::Pending => {
278 self.as_mut()
279 .project_replace(Self::WritingToTempDir(temp_dir, fut, path, vfs));
280 Poll::Pending
281 }
282 }
283 }
284 AtomicDirWriteToAsyncRefFutureProj::PersistingTempDir(mut fut) => {
285 match fut.as_mut().poll(cx) {
286 Poll::Ready(result) => {
287 *self = Self::Done;
288 Poll::Ready(result)
289 }
290 Poll::Pending => {
291 self.as_mut().project_replace(Self::PersistingTempDir(fut));
292 Poll::Pending
293 }
294 }
295 }
296 AtomicDirWriteToAsyncRefFutureProj::DeletingTempDir(mut fut, error) => {
297 match fut.as_mut().poll(cx) {
298 Poll::Ready(_) => Poll::Ready(Err(error)),
299 Poll::Pending => {
300 self.as_mut()
301 .project_replace(Self::DeletingTempDir(fut, error));
302 Poll::Pending
303 }
304 }
305 }
306 AtomicDirWriteToAsyncRefFutureProj::Done => panic!("polled after completion"),
307 AtomicDirWriteToAsyncRefFutureProj::Poison => panic!("future was poisoned"),
308 }
309 }
310}
311
312#[cfg(feature = "async")]
313#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
314impl<'vfs, T, Vfs: WriteSupportingVfsAsync + 'vfs> WriteToAsyncRef<'vfs, Vfs> for AtomicDir<T>
315where
316 T: WriteToAsyncRef<'vfs, Vfs> + Sync + 'static,
317 for<'r> <T as WriteToAsyncRef<'vfs, Vfs>>::Future<'r>:
318 Future<Output = VfsResult<(), Vfs>> + Unpin + Send + 'r,
319 for<'r> Vfs: VfsSupportsTemporaryDirectoriesAsync<'r> + VfsAsync + Send + 'static,
320{
321 type Future<'r>
322 = AtomicDirWriteToAsyncRefFuture<'r, 'vfs, T, Vfs>
323 where
324 'vfs: 'r,
325 Self: 'r;
326
327 fn write_to_async_ref<'r>(
328 &'r self,
329 path: <Vfs::Path as PathType>::OwnedPath,
330 vfs: Pin<&'r Vfs>,
331 ) -> Self::Future<'r>
332 where
333 'vfs: 'r,
334 {
335 AtomicDirWriteToAsyncRefFuture::CreatingTempDir(
336 Box::pin(vfs.create_temporary_directory()),
337 &self.0,
338 path,
339 vfs,
340 )
341 }
342}