cloud_filter/filter/
async_filter.rs

1use std::{future::Future, mem::MaybeUninit, ops::Deref, path::PathBuf};
2
3use crate::{
4    error::{CResult, CloudErrorKind},
5    filter::Request,
6    utility::LocalBoxFuture,
7};
8
9use super::{info, ticket, SyncFilter};
10
11/// Async core functions for implementing a Sync Engine.
12///
13/// [Send] and [Sync] are required as the callback could be invoked from an arbitrary thread, [read
14/// here](https://docs.microsoft.com/en-us/windows/win32/api/cfapi/ne-cfapi-cf_callback_type#remarks).
15pub trait Filter: Send + Sync {
16    /// A placeholder hydration has been requested. This means that the placeholder should be
17    /// populated with its corresponding data on the remote.
18    fn fetch_data(
19        &self,
20        _request: Request,
21        _ticket: ticket::FetchData,
22        _info: info::FetchData,
23    ) -> impl Future<Output = CResult<()>>;
24
25    /// A placeholder hydration request has been cancelled.
26    fn cancel_fetch_data(
27        &self,
28        _request: Request,
29        _info: info::CancelFetchData,
30    ) -> impl Future<Output = ()> {
31        async {}
32    }
33
34    /// Followed by a successful call to [Filter::fetch_data][super::Filter::fetch_data], this callback should verify the integrity of
35    /// the data persisted in the placeholder.
36    ///
37    /// **You** are responsible for validating the data in the placeholder. To approve
38    /// the request, use the ticket provided.
39    ///
40    /// Note that this callback is only called if [HydrationPolicy::ValidationRequired][crate::root::HydrationPolicy::ValidationRequired]
41    /// is specified.
42    fn validate_data(
43        &self,
44        _request: Request,
45        _ticket: ticket::ValidateData,
46        _info: info::ValidateData,
47    ) -> impl Future<Output = CResult<()>> {
48        async { Err(CloudErrorKind::NotSupported) }
49    }
50
51    /// A directory population has been requested. The behavior of this callback is dependent on
52    /// the [PopulationType][crate::root::PopulationType] variant specified during registration.
53    fn fetch_placeholders(
54        &self,
55        _request: Request,
56        _ticket: ticket::FetchPlaceholders,
57        _info: info::FetchPlaceholders,
58    ) -> impl Future<Output = CResult<()>> {
59        async { Err(CloudErrorKind::NotSupported) }
60    }
61
62    /// A directory population request has been cancelled.
63    fn cancel_fetch_placeholders(
64        &self,
65        _request: Request,
66        _info: info::CancelFetchPlaceholders,
67    ) -> impl Future<Output = ()> {
68        async {}
69    }
70
71    /// A placeholder file handle has been opened for read, write, and/or delete
72    /// access.
73    fn opened(&self, _request: Request, _info: info::Opened) -> impl Future<Output = ()> {
74        async {}
75    }
76
77    /// A placeholder file handle that has been previously opened with read, write,
78    /// and/or delete access has been closed.
79    fn closed(&self, _request: Request, _info: info::Closed) -> impl Future<Output = ()> {
80        async {}
81    }
82
83    /// A placeholder dehydration has been requested. This means that all of the data persisted in
84    /// the file will be __completely__ discarded.
85    ///
86    /// The operating system will handle dehydrating placeholder files automatically. However, it
87    /// is up to **you** to approve this. Use the ticket to approve the request.
88    fn dehydrate(
89        &self,
90        _request: Request,
91        _ticket: ticket::Dehydrate,
92        _info: info::Dehydrate,
93    ) -> impl Future<Output = CResult<()>> {
94        async { Err(CloudErrorKind::NotSupported) }
95    }
96
97    /// A placeholder dehydration request has been cancelled.
98    fn dehydrated(&self, _request: Request, _info: info::Dehydrated) -> impl Future<Output = ()> {
99        async {}
100    }
101
102    /// A placeholder file is about to be deleted.
103    ///
104    /// The operating system will handle deleting placeholder files automatically. However, it is
105    /// up to **you** to approve this. Use the ticket to approve the request.
106    fn delete(
107        &self,
108        _request: Request,
109        _ticket: ticket::Delete,
110        _info: info::Delete,
111    ) -> impl Future<Output = CResult<()>> {
112        async { Err(CloudErrorKind::NotSupported) }
113    }
114
115    /// A placeholder file has been deleted.
116    fn deleted(&self, _request: Request, _info: info::Deleted) -> impl Future<Output = ()> {
117        async {}
118    }
119
120    /// A placeholder file is about to be renamed or moved.
121    ///
122    /// The operating system will handle moving and renaming placeholder files automatically.
123    /// However, it is up to **you** to approve this. Use the ticket to approve the
124    /// request.
125    ///
126    /// When the operation is completed, the [Filter::renamed] callback will be called.
127    fn rename(
128        &self,
129        _request: Request,
130        _ticket: ticket::Rename,
131        _info: info::Rename,
132    ) -> impl Future<Output = CResult<()>> {
133        async { Err(CloudErrorKind::NotSupported) }
134    }
135
136    /// A placeholder file has been renamed or moved.
137    fn renamed(&self, _request: Request, _info: info::Renamed) -> impl Future<Output = ()> {
138        async {}
139    }
140
141    /// Placeholder for changed attributes under the sync root.
142    ///
143    /// This callback is implemented using [ReadDirectoryChangesW](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw)
144    /// so it is not provided by the `Cloud Filter APIs`.
145    ///
146    /// This callback is used to detect when a user pins or unpins a placeholder file, etc.
147    ///
148    /// See also [Cloud Files API Frequently Asked Questions](https://www.userfilesystem.com/programming/faq/).
149    fn state_changed(&self, _changes: Vec<PathBuf>) -> impl Future<Output = ()> {
150        async {}
151    }
152}
153
154/// Adapts a [Filter] to the [SyncFilter] trait.
155pub struct AsyncBridge<F, B> {
156    filter: F,
157    block_on: B,
158}
159
160impl<F, B> AsyncBridge<F, B>
161where
162    F: Filter,
163    B: Fn(LocalBoxFuture<'_, ()>) + Send + Sync,
164{
165    pub(crate) fn new(filter: F, block_on: B) -> Self {
166        Self { filter, block_on }
167    }
168}
169
170impl<F, B> SyncFilter for AsyncBridge<F, B>
171where
172    F: Filter,
173    B: Fn(LocalBoxFuture<'_, ()>) + Send + Sync,
174{
175    fn fetch_data(
176        &self,
177        request: Request,
178        ticket: ticket::FetchData,
179        info: info::FetchData,
180    ) -> CResult<()> {
181        let mut ret = MaybeUninit::zeroed();
182        (self.block_on)(Box::pin(async {
183            ret.write(self.filter.fetch_data(request, ticket, info).await);
184        }));
185
186        unsafe { ret.assume_init() }
187    }
188
189    fn cancel_fetch_data(&self, request: Request, info: info::CancelFetchData) {
190        (self.block_on)(Box::pin(self.filter.cancel_fetch_data(request, info)))
191    }
192
193    fn validate_data(
194        &self,
195        request: Request,
196        ticket: ticket::ValidateData,
197        info: info::ValidateData,
198    ) -> CResult<()> {
199        let mut ret = MaybeUninit::zeroed();
200        (self.block_on)(Box::pin(async {
201            ret.write(self.filter.validate_data(request, ticket, info).await);
202        }));
203
204        unsafe { ret.assume_init() }
205    }
206
207    fn fetch_placeholders(
208        &self,
209        request: Request,
210        ticket: ticket::FetchPlaceholders,
211        info: info::FetchPlaceholders,
212    ) -> CResult<()> {
213        let mut ret = MaybeUninit::zeroed();
214        (self.block_on)(Box::pin(async {
215            ret.write(self.filter.fetch_placeholders(request, ticket, info).await);
216        }));
217
218        unsafe { ret.assume_init() }
219    }
220
221    fn cancel_fetch_placeholders(&self, request: Request, info: info::CancelFetchPlaceholders) {
222        (self.block_on)(Box::pin(
223            self.filter.cancel_fetch_placeholders(request, info),
224        ))
225    }
226
227    fn opened(&self, request: Request, info: info::Opened) {
228        (self.block_on)(Box::pin(self.filter.opened(request, info)))
229    }
230
231    fn closed(&self, request: Request, info: info::Closed) {
232        (self.block_on)(Box::pin(self.filter.closed(request, info)))
233    }
234
235    fn dehydrate(
236        &self,
237        request: Request,
238        ticket: ticket::Dehydrate,
239        info: info::Dehydrate,
240    ) -> CResult<()> {
241        let mut ret = MaybeUninit::zeroed();
242        (self.block_on)(Box::pin(async {
243            ret.write(self.filter.dehydrate(request, ticket, info).await);
244        }));
245
246        unsafe { ret.assume_init() }
247    }
248
249    fn dehydrated(&self, request: Request, info: info::Dehydrated) {
250        (self.block_on)(Box::pin(self.filter.dehydrated(request, info)))
251    }
252
253    fn delete(&self, request: Request, ticket: ticket::Delete, info: info::Delete) -> CResult<()> {
254        let mut ret = MaybeUninit::zeroed();
255        (self.block_on)(Box::pin(async {
256            ret.write(self.filter.delete(request, ticket, info).await);
257        }));
258
259        unsafe { ret.assume_init() }
260    }
261
262    fn deleted(&self, request: Request, info: info::Deleted) {
263        (self.block_on)(Box::pin(self.filter.deleted(request, info)))
264    }
265
266    fn rename(&self, request: Request, ticket: ticket::Rename, info: info::Rename) -> CResult<()> {
267        let mut ret = MaybeUninit::zeroed();
268        (self.block_on)(Box::pin(async {
269            ret.write(self.filter.rename(request, ticket, info).await);
270        }));
271
272        unsafe { ret.assume_init() }
273    }
274
275    fn renamed(&self, request: Request, info: info::Renamed) {
276        (self.block_on)(Box::pin(self.filter.renamed(request, info)))
277    }
278
279    fn state_changed(&self, changes: Vec<PathBuf>) {
280        (self.block_on)(Box::pin(self.filter.state_changed(changes)))
281    }
282}
283
284impl<F, B> Deref for AsyncBridge<F, B>
285where
286    F: Filter,
287    B: Fn(LocalBoxFuture<'_, ()>) + Send + Sync,
288{
289    type Target = F;
290
291    fn deref(&self) -> &Self::Target {
292        &self.filter
293    }
294}