dir_structure/
option.rs

1//! `Option<T>` implementations.
2
3use std::pin::Pin;
4#[cfg(feature = "async")]
5use std::task::Context;
6#[cfg(feature = "async")]
7use std::task::Poll;
8
9#[cfg(feature = "async")]
10use pin_project::pin_project;
11
12use crate::error::VfsResult;
13use crate::prelude::*;
14#[cfg(feature = "async")]
15use crate::traits::async_vfs::VfsAsync;
16#[cfg(feature = "async")]
17use crate::traits::async_vfs::WriteSupportingVfsAsync;
18#[cfg(feature = "resolve-path")]
19use crate::traits::resolve::DynamicHasField;
20#[cfg(feature = "resolve-path")]
21use crate::traits::resolve::HAS_FIELD_MAX_LEN;
22#[cfg(feature = "resolve-path")]
23use crate::traits::resolve::HasField;
24use crate::traits::vfs;
25#[cfg(feature = "resolve-path")]
26use crate::traits::vfs::OwnedPathType;
27use crate::traits::vfs::PathType;
28
29impl<'a, T, Vfs: vfs::Vfs<'a>> ReadFrom<'a, Vfs> for Option<T>
30where
31    T: ReadFrom<'a, Vfs>,
32{
33    fn read_from(path: &Vfs::Path, vfs: Pin<&'a Vfs>) -> VfsResult<Self, Vfs>
34    where
35        Self: Sized,
36    {
37        if vfs.exists(path)? {
38            T::read_from(path, vfs).map(Some)
39        } else {
40            Ok(None)
41        }
42    }
43}
44
45#[cfg(feature = "async")]
46#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
47#[pin_project(project_replace = OptionReadFromAsyncFutureOwnProj)]
48#[doc(hidden)]
49pub enum OptionReadFromAsyncFuture<'a, T, P: PathType + ?Sized + 'a, Vfs: VfsAsync<Path = P> + 'a>
50where
51    T: ReadFromAsync<'a, Vfs> + 'static,
52    Vfs::ExistsFuture<'a>: Future<Output = VfsResult<bool, Vfs>>,
53    T::Future: Future<Output = VfsResult<T, Vfs>> + Unpin,
54{
55    Poison,
56    Check {
57        path: P::OwnedPath,
58        check_fut: Pin<Box<Vfs::ExistsFuture<'a>>>,
59        vfs: Pin<&'a Vfs>,
60    },
61    HasContents {
62        inner: T::Future,
63        vfs: Pin<&'a Vfs>,
64    },
65    NoContents,
66}
67
68#[cfg(feature = "async")]
69#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
70impl<'a, T, Vfs: VfsAsync<Path = P> + 'a, P: PathType + ?Sized + 'a> Future
71    for OptionReadFromAsyncFuture<'a, T, P, Vfs>
72where
73    T: ReadFromAsync<'a, Vfs> + 'static,
74    T::Future: Future<Output = VfsResult<T, Vfs>> + Unpin,
75{
76    type Output = VfsResult<Option<T>, Vfs>;
77
78    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
79        use std::task::Poll;
80
81        let this = self.as_mut().project_replace(Self::Poison);
82        match this {
83            OptionReadFromAsyncFutureOwnProj::Check {
84                path,
85                mut check_fut,
86                vfs,
87            } => {
88                match check_fut.as_mut().poll(cx) {
89                    Poll::Ready(Ok(true)) => {
90                        self.project_replace(Self::HasContents {
91                            inner: T::read_from_async(path, vfs),
92                            vfs,
93                        });
94                        cx.waker().wake_by_ref();
95                        Poll::Pending
96                    }
97                    Poll::Ready(Ok(false)) => {
98                        // If the path does not exist, we return None
99                        Poll::Ready(Ok(None))
100                    }
101                    Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
102                    Poll::Pending => {
103                        // If the check is still pending, we return Pending
104                        self.project_replace(Self::Check {
105                            path,
106                            check_fut,
107                            vfs,
108                        });
109                        Poll::Pending
110                    }
111                }
112            }
113            OptionReadFromAsyncFutureOwnProj::HasContents { mut inner, vfs } => {
114                match Pin::new(&mut inner).poll(cx) {
115                    Poll::Ready(v) => Poll::Ready(v.map(Some)),
116                    Poll::Pending => {
117                        self.project_replace(Self::HasContents { inner, vfs });
118                        Poll::Pending
119                    }
120                }
121            }
122            OptionReadFromAsyncFutureOwnProj::NoContents => {
123                // If there are no contents, we return None
124                Poll::Ready(Ok(None))
125            }
126            OptionReadFromAsyncFutureOwnProj::Poison => {
127                panic!("OptionReadFromAsyncFuture was polled after it was replaced with Poison");
128            }
129        }
130    }
131}
132
133#[cfg(feature = "async")]
134#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
135impl<'a, T, Vfs: VfsAsync<Path = P> + 'a, P: PathType + ?Sized + 'a> ReadFromAsync<'a, Vfs>
136    for Option<T>
137where
138    T: ReadFromAsync<'a, Vfs> + 'static,
139    T::Future: Future<Output = VfsResult<T, Vfs>> + Unpin + 'a,
140{
141    type Future
142        = OptionReadFromAsyncFuture<'a, T, P, Vfs>
143    where
144        Self: 'static;
145
146    fn read_from_async(path: P::OwnedPath, vfs: Pin<&'a Vfs>) -> Self::Future {
147        OptionReadFromAsyncFuture::Check {
148            check_fut: Box::pin(vfs.exists(path.clone())),
149            path,
150            vfs,
151        }
152    }
153}
154
155impl<'vfs, T, P: PathType + ?Sized + 'vfs, Vfs: vfs::WriteSupportingVfs<'vfs, Path = P>>
156    WriteTo<'vfs, Vfs> for Option<T>
157where
158    T: WriteTo<'vfs, Vfs>,
159{
160    fn write_to(&self, path: &P, vfs: Pin<&'vfs Vfs>) -> VfsResult<(), Vfs> {
161        if let Some(v) = self {
162            v.write_to(path, vfs)
163        } else {
164            Ok(())
165        }
166    }
167}
168
169#[cfg(feature = "async")]
170#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
171#[pin_project(project = OptionWriteToAsyncFutureProj)]
172#[doc(hidden)]
173pub enum OptionWriteToAsyncFuture<'a, T, Vfs: WriteSupportingVfsAsync + 'a>
174where
175    T: WriteToAsync<'a, Vfs> + 'static,
176{
177    HasContents {
178        #[pin]
179        inner: <T as WriteToAsync<'a, Vfs>>::Future,
180    },
181    NoContents,
182}
183
184#[cfg(feature = "async")]
185#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
186impl<'a, T, Vfs: WriteSupportingVfsAsync<Path = P>, P: PathType + ?Sized + 'a> Future
187    for OptionWriteToAsyncFuture<'a, T, Vfs>
188where
189    T: WriteToAsync<'a, Vfs> + 'static,
190{
191    type Output = VfsResult<(), Vfs>;
192
193    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
194        let this = self.project();
195        match this {
196            OptionWriteToAsyncFutureProj::HasContents { inner } => inner.poll(cx),
197            OptionWriteToAsyncFutureProj::NoContents => Poll::Ready(Ok(())),
198        }
199    }
200}
201
202#[cfg(feature = "async")]
203#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
204impl<'a, T, Vfs: WriteSupportingVfsAsync + 'static> WriteToAsync<'a, Vfs> for Option<T>
205where
206    T: WriteToAsync<'a, Vfs> + Send + 'static,
207{
208    type Future = OptionWriteToAsyncFuture<'a, T, Vfs>;
209
210    fn write_to_async(
211        self,
212        path: <Vfs::Path as PathType>::OwnedPath,
213        vfs: Pin<&'a Vfs>,
214    ) -> Self::Future {
215        if let Some(v) = self {
216            OptionWriteToAsyncFuture::HasContents {
217                inner: v.write_to_async(path, vfs),
218            }
219        } else {
220            OptionWriteToAsyncFuture::NoContents
221        }
222    }
223}
224
225#[cfg(feature = "resolve-path")]
226#[cfg_attr(docsrs, doc(cfg(feature = "resolve-path")))]
227impl<const NAME: [char; HAS_FIELD_MAX_LEN], T> HasField<NAME> for Option<T>
228where
229    T: HasField<NAME>,
230{
231    type Inner = <T as HasField<NAME>>::Inner;
232
233    fn resolve_path<P: OwnedPathType>(p: P) -> P {
234        T::resolve_path(p)
235    }
236}
237
238#[cfg(feature = "resolve-path")]
239#[cfg_attr(docsrs, doc(cfg(feature = "resolve-path")))]
240impl<T> DynamicHasField for Option<T>
241where
242    T: DynamicHasField,
243{
244    type Inner = <T as DynamicHasField>::Inner;
245
246    fn resolve_path<P: OwnedPathType>(p: P, name: &str) -> P {
247        T::resolve_path(p, name)
248    }
249}