1use 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 Poll::Ready(Ok(None))
100 }
101 Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
102 Poll::Pending => {
103 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 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}