use std::{
fs::File,
mem,
ops::{Bound, RangeBounds},
os::windows::{io::AsRawHandle, prelude::RawHandle},
};
use widestring::U16CStr;
use windows::{
core,
Win32::{
Foundation::HANDLE,
Storage::CloudFilters::{
self, CfDehydratePlaceholder, CF_SYNC_PROVIDER_STATUS, CF_SYNC_ROOT_STANDARD_INFO,
},
},
};
use crate::sealed::Sealed;
pub trait FileExt: AsRawHandle + Sealed {
fn dehydrate<T: RangeBounds<u64>>(&self, range: T) -> core::Result<()> {
dehydrate(self.as_raw_handle(), range, false)
}
fn background_dehydrate<T: RangeBounds<u64>>(&self, range: T) -> core::Result<()> {
dehydrate(self.as_raw_handle(), range, true)
}
fn in_sync_root() -> core::Result<bool> {
todo!()
}
}
fn dehydrate<T: RangeBounds<u64>>(
handle: RawHandle,
range: T,
background: bool,
) -> core::Result<()> {
unsafe {
CfDehydratePlaceholder(
HANDLE(handle),
match range.start_bound() {
Bound::Included(x) => *x as i64,
Bound::Excluded(x) => x.saturating_add(1) as i64,
Bound::Unbounded => 0,
},
match range.end_bound() {
Bound::Included(x) => *x as i64,
Bound::Excluded(x) => x.saturating_sub(1) as i64,
Bound::Unbounded => -1,
},
if background {
CloudFilters::CF_DEHYDRATE_FLAG_NONE
} else {
CloudFilters::CF_DEHYDRATE_FLAG_BACKGROUND
},
None,
)
}
}
impl FileExt for File {}
impl Sealed for File {}
#[derive(Debug)]
pub struct SyncRootInfo {
data: Vec<u8>,
info: *const CF_SYNC_ROOT_STANDARD_INFO,
}
impl SyncRootInfo {
pub fn file_id(&self) -> u64 {
unsafe { &*self.info }.SyncRootFileId as u64
}
pub fn hardlinks_allowed(&self) -> bool {
unsafe { &*self.info }.HardLinkPolicy == CloudFilters::CF_HARDLINK_POLICY_ALLOWED
}
pub fn status(&self) -> ProviderStatus {
unsafe { &*self.info }.ProviderStatus.into()
}
pub fn provider_name(&self) -> &U16CStr {
U16CStr::from_slice_truncate(unsafe { &*self.info }.ProviderName.as_slice()).unwrap()
}
pub fn version(&self) -> &U16CStr {
U16CStr::from_slice_truncate(unsafe { &*self.info }.ProviderVersion.as_slice()).unwrap()
}
pub fn blob(&self) -> &[u8] {
&self.data[(mem::size_of::<CF_SYNC_ROOT_STANDARD_INFO>() + 1)..]
}
}
#[derive(Debug, Clone, Copy)]
pub enum ProviderStatus {
Disconnected,
Idle,
PopulateNamespace,
PopulateMetadata,
PopulateContent,
SyncIncremental,
SyncFull,
ConnectivityLost,
Terminated,
Error,
}
impl From<CF_SYNC_PROVIDER_STATUS> for ProviderStatus {
fn from(status: CF_SYNC_PROVIDER_STATUS) -> Self {
match status {
CloudFilters::CF_PROVIDER_STATUS_DISCONNECTED => Self::Disconnected,
CloudFilters::CF_PROVIDER_STATUS_IDLE => Self::Idle,
CloudFilters::CF_PROVIDER_STATUS_POPULATE_NAMESPACE => Self::PopulateNamespace,
CloudFilters::CF_PROVIDER_STATUS_POPULATE_METADATA => Self::PopulateContent,
CloudFilters::CF_PROVIDER_STATUS_POPULATE_CONTENT => Self::PopulateContent,
CloudFilters::CF_PROVIDER_STATUS_SYNC_INCREMENTAL => Self::SyncIncremental,
CloudFilters::CF_PROVIDER_STATUS_SYNC_FULL => Self::SyncFull,
CloudFilters::CF_PROVIDER_STATUS_CONNECTIVITY_LOST => Self::ConnectivityLost,
CloudFilters::CF_PROVIDER_STATUS_TERMINATED => Self::Terminated,
CloudFilters::CF_PROVIDER_STATUS_ERROR => Self::Error,
_ => unreachable!(),
}
}
}
impl From<ProviderStatus> for CF_SYNC_PROVIDER_STATUS {
fn from(status: ProviderStatus) -> Self {
match status {
ProviderStatus::Disconnected => CloudFilters::CF_PROVIDER_STATUS_DISCONNECTED,
ProviderStatus::Idle => CloudFilters::CF_PROVIDER_STATUS_IDLE,
ProviderStatus::PopulateNamespace => {
CloudFilters::CF_PROVIDER_STATUS_POPULATE_NAMESPACE
}
ProviderStatus::PopulateMetadata => CloudFilters::CF_PROVIDER_STATUS_POPULATE_METADATA,
ProviderStatus::PopulateContent => CloudFilters::CF_PROVIDER_STATUS_POPULATE_CONTENT,
ProviderStatus::SyncIncremental => CloudFilters::CF_PROVIDER_STATUS_SYNC_INCREMENTAL,
ProviderStatus::SyncFull => CloudFilters::CF_PROVIDER_STATUS_SYNC_FULL,
ProviderStatus::ConnectivityLost => CloudFilters::CF_PROVIDER_STATUS_CONNECTIVITY_LOST,
ProviderStatus::Terminated => CloudFilters::CF_PROVIDER_STATUS_TERMINATED,
ProviderStatus::Error => CloudFilters::CF_PROVIDER_STATUS_ERROR,
}
}
}