1use std::{
2 fs::File,
3 mem,
4 ops::{Bound, RangeBounds},
5 os::windows::{io::AsRawHandle, prelude::RawHandle},
6};
7
8use widestring::U16CStr;
9use windows::{
10 core,
11 Win32::{
12 Foundation::HANDLE,
13 Storage::CloudFilters::{
14 self, CfDehydratePlaceholder, CF_SYNC_PROVIDER_STATUS, CF_SYNC_ROOT_STANDARD_INFO,
15 },
16 },
17};
18
19use crate::sealed::Sealed;
20
21pub trait FileExt: AsRawHandle + Sealed {
23 fn dehydrate<T: RangeBounds<u64>>(&self, range: T) -> core::Result<()> {
25 dehydrate(self.as_raw_handle(), range, false)
26 }
27
28 fn background_dehydrate<T: RangeBounds<u64>>(&self, range: T) -> core::Result<()> {
31 dehydrate(self.as_raw_handle(), range, true)
32 }
33
34 fn in_sync_root() -> core::Result<bool> {
36 todo!()
38 }
39}
40
41fn dehydrate<T: RangeBounds<u64>>(
44 handle: RawHandle,
45 range: T,
46 background: bool,
47) -> core::Result<()> {
48 unsafe {
49 CfDehydratePlaceholder(
50 HANDLE(handle),
51 match range.start_bound() {
52 Bound::Included(x) => *x as i64,
53 Bound::Excluded(x) => x.saturating_add(1) as i64,
54 Bound::Unbounded => 0,
55 },
56 match range.end_bound() {
57 Bound::Included(x) => *x as i64,
58 Bound::Excluded(x) => x.saturating_sub(1) as i64,
59 Bound::Unbounded => -1,
61 },
62 if background {
63 CloudFilters::CF_DEHYDRATE_FLAG_NONE
64 } else {
65 CloudFilters::CF_DEHYDRATE_FLAG_BACKGROUND
66 },
67 None,
68 )
69 }
70}
71
72impl FileExt for File {}
73
74impl Sealed for File {}
75
76#[derive(Debug)]
78pub struct SyncRootInfo {
79 data: Vec<u8>,
80 info: *const CF_SYNC_ROOT_STANDARD_INFO,
81}
82
83impl SyncRootInfo {
85 pub fn file_id(&self) -> u64 {
87 unsafe { &*self.info }.SyncRootFileId as u64
88 }
89
90 pub fn hardlinks_allowed(&self) -> bool {
112 unsafe { &*self.info }.HardLinkPolicy == CloudFilters::CF_HARDLINK_POLICY_ALLOWED
113 }
114
115 pub fn status(&self) -> ProviderStatus {
117 unsafe { &*self.info }.ProviderStatus.into()
118 }
119
120 pub fn provider_name(&self) -> &U16CStr {
122 U16CStr::from_slice_truncate(unsafe { &*self.info }.ProviderName.as_slice()).unwrap()
123 }
124
125 pub fn version(&self) -> &U16CStr {
127 U16CStr::from_slice_truncate(unsafe { &*self.info }.ProviderVersion.as_slice()).unwrap()
128 }
129
130 pub fn blob(&self) -> &[u8] {
132 &self.data[(mem::size_of::<CF_SYNC_ROOT_STANDARD_INFO>() + 1)..]
133 }
134}
135
136#[derive(Debug, Clone, Copy)]
138pub enum ProviderStatus {
139 Disconnected,
141 Idle,
143 PopulateNamespace,
145 PopulateMetadata,
147 PopulateContent,
149 SyncIncremental,
151 SyncFull,
153 ConnectivityLost,
155 Terminated,
159 Error,
161}
162
163impl From<CF_SYNC_PROVIDER_STATUS> for ProviderStatus {
164 fn from(status: CF_SYNC_PROVIDER_STATUS) -> Self {
165 match status {
166 CloudFilters::CF_PROVIDER_STATUS_DISCONNECTED => Self::Disconnected,
167 CloudFilters::CF_PROVIDER_STATUS_IDLE => Self::Idle,
168 CloudFilters::CF_PROVIDER_STATUS_POPULATE_NAMESPACE => Self::PopulateNamespace,
169 CloudFilters::CF_PROVIDER_STATUS_POPULATE_METADATA => Self::PopulateContent,
170 CloudFilters::CF_PROVIDER_STATUS_POPULATE_CONTENT => Self::PopulateContent,
171 CloudFilters::CF_PROVIDER_STATUS_SYNC_INCREMENTAL => Self::SyncIncremental,
172 CloudFilters::CF_PROVIDER_STATUS_SYNC_FULL => Self::SyncFull,
173 CloudFilters::CF_PROVIDER_STATUS_CONNECTIVITY_LOST => Self::ConnectivityLost,
174 CloudFilters::CF_PROVIDER_STATUS_TERMINATED => Self::Terminated,
176 CloudFilters::CF_PROVIDER_STATUS_ERROR => Self::Error,
177 _ => unreachable!(),
178 }
179 }
180}
181
182impl From<ProviderStatus> for CF_SYNC_PROVIDER_STATUS {
183 fn from(status: ProviderStatus) -> Self {
184 match status {
185 ProviderStatus::Disconnected => CloudFilters::CF_PROVIDER_STATUS_DISCONNECTED,
186 ProviderStatus::Idle => CloudFilters::CF_PROVIDER_STATUS_IDLE,
187 ProviderStatus::PopulateNamespace => {
188 CloudFilters::CF_PROVIDER_STATUS_POPULATE_NAMESPACE
189 }
190 ProviderStatus::PopulateMetadata => CloudFilters::CF_PROVIDER_STATUS_POPULATE_METADATA,
191 ProviderStatus::PopulateContent => CloudFilters::CF_PROVIDER_STATUS_POPULATE_CONTENT,
192 ProviderStatus::SyncIncremental => CloudFilters::CF_PROVIDER_STATUS_SYNC_INCREMENTAL,
193 ProviderStatus::SyncFull => CloudFilters::CF_PROVIDER_STATUS_SYNC_FULL,
194 ProviderStatus::ConnectivityLost => CloudFilters::CF_PROVIDER_STATUS_CONNECTIVITY_LOST,
195 ProviderStatus::Terminated => CloudFilters::CF_PROVIDER_STATUS_TERMINATED,
197 ProviderStatus::Error => CloudFilters::CF_PROVIDER_STATUS_ERROR,
198 }
199 }
200}