cloud_filter/filter/
ticket.rs

1use std::ops::Range;
2
3use windows::{
4    core,
5    Win32::Storage::CloudFilters::{CfReportProviderProgress, CF_CONNECTION_KEY},
6};
7
8use crate::{
9    command::{self, Command},
10    filter::{RawConnectionKey, RawTransferKey},
11    placeholder_file::PlaceholderFile,
12    sealed, utility,
13};
14
15/// A ticket for the [SyncFilter::fetch_data][crate::filter::SyncFilter::fetch_data] callback.
16#[derive(Debug)]
17pub struct FetchData {
18    connection_key: RawConnectionKey,
19    transfer_key: RawTransferKey,
20}
21
22impl FetchData {
23    /// Create a new [FetchData].
24    pub(crate) fn new(connection_key: RawConnectionKey, transfer_key: RawTransferKey) -> Self {
25        Self {
26            connection_key,
27            transfer_key,
28        }
29    }
30
31    /// Displays a progress bar next to the file in the file explorer to show the progress of the
32    /// current operation. In addition, the standard Windows file progress dialog will open
33    /// displaying the speed and progress based on the values set. During background hydrations,
34    /// an interactive toast will appear notifying the user of an operation with a progress bar.
35    pub fn report_progress(&self, total: u64, completed: u64) -> core::Result<()> {
36        unsafe {
37            CfReportProviderProgress(
38                CF_CONNECTION_KEY(self.connection_key),
39                self.transfer_key,
40                total as i64,
41                completed as i64,
42            )
43        }?;
44
45        Ok(())
46    }
47
48    // TODO: response command::Update
49}
50
51impl utility::ReadAt for FetchData {
52    /// Read data at an offset from a placeholder file.
53    ///
54    /// This method is equivalent to calling `CfExecute` with `CF_OPERATION_TYPE_RETRIEVE_DATA`.
55    fn read_at(&self, buf: &mut [u8], offset: u64) -> core::Result<u64> {
56        command::Read {
57            buffer: buf,
58            position: offset,
59        }
60        .execute(self.connection_key, self.transfer_key)
61    }
62}
63
64impl utility::WriteAt for FetchData {
65    /// Write data at an offset to a placeholder file.
66    ///
67    /// The buffer passed must be 4KiB in length or end on the logical file size. Unfortunately,
68    /// this is a restriction of the operating system.
69    ///
70    /// This method is equivalent to calling `CfExecute` with `CF_OPERATION_TYPE_TRANSFER_DATA`.
71    fn write_at(&self, buf: &[u8], offset: u64) -> core::Result<()> {
72        command::Write {
73            buffer: buf,
74            position: offset,
75        }
76        .execute(self.connection_key, self.transfer_key)
77    }
78}
79
80impl sealed::Sealed for FetchData {}
81
82/// A ticket for the [SyncFilter::validate_data][crate::filter::SyncFilter::validate_data] callback.
83#[derive(Debug)]
84pub struct ValidateData {
85    connection_key: RawConnectionKey,
86    transfer_key: RawTransferKey,
87}
88
89impl ValidateData {
90    /// Create a new [ValidateData].
91    pub(crate) fn new(connection_key: RawConnectionKey, transfer_key: RawTransferKey) -> Self {
92        Self {
93            connection_key,
94            transfer_key,
95        }
96    }
97
98    /// Validates the data range in the placeholder file is valid.
99    ///
100    /// This method is equivalent to calling `CfExecute` with `CF_OPERATION_TYPE_ACK_DATA`.
101    // TODO: make this generic over a RangeBounds
102    // if the range specified is past the current file length, will it consider that range to be validated?
103    // https://docs.microsoft.com/en-us/answers/questions/750302/if-the-ackdata-field-of-cf-operation-parameters-is.html
104    pub fn pass(&self, range: Range<u64>) -> core::Result<()> {
105        command::Validate { range }.execute(self.connection_key, self.transfer_key)
106    }
107
108    // TODO: response command::Update
109}
110
111impl utility::ReadAt for ValidateData {
112    /// Read data at an offset from a placeholder file.
113    ///
114    /// This method is equivalent to calling `CfExecute` with `CF_OPERATION_TYPE_RETRIEVE_DATA`.
115    ///
116    /// The bytes returned will ALWAYS be the length of the buffer passed in. The operating
117    /// system provides these guarantees.
118    fn read_at(&self, buf: &mut [u8], offset: u64) -> core::Result<u64> {
119        command::Read {
120            buffer: buf,
121            position: offset,
122        }
123        .execute(self.connection_key, self.transfer_key)
124    }
125}
126
127impl sealed::Sealed for ValidateData {}
128
129/// A ticket for the [SyncFilter::fetch_placeholders][crate::filter::SyncFilter::fetch_placeholders] callback.
130#[derive(Debug)]
131pub struct FetchPlaceholders {
132    connection_key: RawConnectionKey,
133    transfer_key: RawTransferKey,
134}
135
136impl FetchPlaceholders {
137    /// Create a new [FetchPlaceholders].
138    pub(crate) fn new(connection_key: RawConnectionKey, transfer_key: RawTransferKey) -> Self {
139        Self {
140            connection_key,
141            transfer_key,
142        }
143    }
144
145    /// Creates a list of placeholder files/directorys on the file system.
146    ///
147    /// The value returned is the final [Usn][crate::usn::Usn] (and if they succeeded) after each placeholder is created.
148    pub fn pass_with_placeholder(&self, placeholders: &mut [PlaceholderFile]) -> core::Result<()> {
149        command::CreatePlaceholders {
150            total: placeholders.len() as _,
151            placeholders,
152        }
153        .execute(self.connection_key, self.transfer_key)
154    }
155}
156
157/// A ticket for the [SyncFilter::dehydrate][crate::filter::SyncFilter::dehydrate] callback.
158#[derive(Debug)]
159pub struct Dehydrate {
160    connection_key: RawConnectionKey,
161    transfer_key: RawTransferKey,
162}
163
164impl Dehydrate {
165    /// Create a new [Dehydrate].
166    pub(crate) fn new(connection_key: RawConnectionKey, transfer_key: RawTransferKey) -> Self {
167        Self {
168            connection_key,
169            transfer_key,
170        }
171    }
172
173    /// Confirms dehydration of the file.
174    pub fn pass(&self) -> core::Result<()> {
175        command::Dehydrate { blob: &[] }.execute(self.connection_key, self.transfer_key)
176    }
177
178    /// Confirms dehydration of the file and updates its file blob.
179    pub fn pass_with_blob(&self, blob: &[u8]) -> core::Result<()> {
180        command::Dehydrate { blob }.execute(self.connection_key, self.transfer_key)
181    }
182}
183
184/// A ticket for the [SyncFilter::delete][crate::filter::SyncFilter::delete] callback.
185#[derive(Debug)]
186pub struct Delete {
187    connection_key: RawConnectionKey,
188    transfer_key: RawTransferKey,
189}
190
191impl Delete {
192    /// Create a new [Delete].
193    pub(crate) fn new(connection_key: RawConnectionKey, transfer_key: RawTransferKey) -> Self {
194        Self {
195            connection_key,
196            transfer_key,
197        }
198    }
199
200    /// Confirms deletion of the file.
201    pub fn pass(&self) -> core::Result<()> {
202        command::Delete.execute(self.connection_key, self.transfer_key)
203    }
204}
205
206/// A ticket for the [SyncFilter::rename][crate::filter::SyncFilter::rename] callback.
207#[derive(Debug)]
208pub struct Rename {
209    connection_key: RawConnectionKey,
210    transfer_key: RawTransferKey,
211}
212
213impl Rename {
214    /// Create a new [Rename].
215    pub(crate) fn new(connection_key: RawConnectionKey, transfer_key: RawTransferKey) -> Self {
216        Self {
217            connection_key,
218            transfer_key,
219        }
220    }
221
222    /// Confirms the rename/move of a file.
223    pub fn pass(&self) -> core::Result<()> {
224        command::Rename.execute(self.connection_key, self.transfer_key)
225    }
226}