use std::{ffi::OsString, fmt::Debug, ops::Range, path::PathBuf};
use nt_time::FileTime;
use widestring::U16CStr;
use windows::Win32::Storage::CloudFilters::{
self, CF_CALLBACK_DEHYDRATION_REASON, CF_CALLBACK_PARAMETERS_0_0, CF_CALLBACK_PARAMETERS_0_1,
CF_CALLBACK_PARAMETERS_0_10, CF_CALLBACK_PARAMETERS_0_11, CF_CALLBACK_PARAMETERS_0_2,
CF_CALLBACK_PARAMETERS_0_3, CF_CALLBACK_PARAMETERS_0_4, CF_CALLBACK_PARAMETERS_0_5,
CF_CALLBACK_PARAMETERS_0_6, CF_CALLBACK_PARAMETERS_0_7, CF_CALLBACK_PARAMETERS_0_8,
CF_CALLBACK_PARAMETERS_0_9,
};
pub struct FetchData(pub(crate) CF_CALLBACK_PARAMETERS_0_6);
impl FetchData {
pub fn interrupted_hydration(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_FETCH_DATA_FLAG_RECOVERY).0 != 0
}
pub fn explicit_hydration(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_FETCH_DATA_FLAG_EXPLICIT_HYDRATION).0 != 0
}
pub fn required_file_range(&self) -> Range<u64> {
(self.0.RequiredFileOffset as u64)
..(self.0.RequiredFileOffset + self.0.RequiredLength) as u64
}
pub fn optional_file_range(&self) -> Range<u64> {
(self.0.OptionalFileOffset as u64)
..(self.0.OptionalFileOffset + self.0.OptionalLength) as u64
}
pub fn last_dehydration_time(&self) -> FileTime {
self.0.LastDehydrationTime.try_into().unwrap()
}
pub fn last_dehydration_reason(&self) -> Option<DehydrationReason> {
DehydrationReason::from_win32(self.0.LastDehydrationReason)
}
}
impl Debug for FetchData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FetchData")
.field("interrupted_hydration", &self.interrupted_hydration())
.field("required_file_range", &self.required_file_range())
.field("optional_file_range", &self.optional_file_range())
.field("last_dehydration_time", &self.last_dehydration_time())
.field("last_dehydration_reason", &self.last_dehydration_reason())
.finish()
}
}
pub struct CancelFetchData(pub(crate) CF_CALLBACK_PARAMETERS_0_0);
impl CancelFetchData {
pub fn timeout(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_CANCEL_FLAG_IO_TIMEOUT).0 != 0
}
pub fn user_cancelled(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_CANCEL_FLAG_IO_ABORTED).0 != 0
}
pub fn file_range(&self) -> Range<u64> {
let range = unsafe { self.0.Anonymous.FetchData };
(range.FileOffset as u64)..(range.FileOffset + range.Length) as u64
}
}
impl Debug for CancelFetchData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CancelFetchData")
.field("timeout", &self.timeout())
.field("user_cancelled", &self.user_cancelled())
.field("file_range", &self.file_range())
.finish()
}
}
pub struct ValidateData(pub(crate) CF_CALLBACK_PARAMETERS_0_11);
impl ValidateData {
pub fn explicit_hydration(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_VALIDATE_DATA_FLAG_EXPLICIT_HYDRATION).0 != 0
}
pub fn file_range(&self) -> Range<u64> {
(self.0.RequiredFileOffset as u64)
..(self.0.RequiredFileOffset + self.0.RequiredLength) as u64
}
}
impl Debug for ValidateData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ValidateData")
.field("explicit_hydration", &self.explicit_hydration())
.field("file_range", &self.file_range())
.finish()
}
}
pub struct FetchPlaceholders(pub(crate) CF_CALLBACK_PARAMETERS_0_7);
impl FetchPlaceholders {
#[cfg(feature = "globs")]
pub fn pattern(&self) -> Result<globset::Glob, globset::Error> {
let pattern = unsafe { U16CStr::from_ptr_str(self.0.Pattern.0) }.to_string_lossy();
globset::Glob::new(&pattern)
}
#[cfg(not(feature = "globs"))]
pub fn pattern(&self) -> String {
unsafe { U16CStr::from_ptr_str(self.0.Pattern.0) }.to_string_lossy()
}
}
impl Debug for FetchPlaceholders {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FetchPlaceholders")
.field("pattern", &self.pattern())
.finish()
}
}
pub struct CancelFetchPlaceholders(pub(crate) CF_CALLBACK_PARAMETERS_0_0);
impl CancelFetchPlaceholders {
pub fn timeout(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_CANCEL_FLAG_IO_TIMEOUT).0 != 0
}
pub fn user_cancelled(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_CANCEL_FLAG_IO_ABORTED).0 != 0
}
}
impl Debug for CancelFetchPlaceholders {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CancelFetchPlaceholders")
.field("timeout", &self.timeout())
.field("user_cancelled", &self.user_cancelled())
.finish()
}
}
pub struct Opened(pub(crate) CF_CALLBACK_PARAMETERS_0_8);
impl Opened {
pub fn metadata_corrupt(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_OPEN_COMPLETION_FLAG_PLACEHOLDER_UNKNOWN).0 != 0
}
pub fn metadata_unsupported(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_OPEN_COMPLETION_FLAG_PLACEHOLDER_UNSUPPORTED).0
!= 0
}
}
impl Debug for Opened {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Opened")
.field("metadata_corrupt", &self.metadata_corrupt())
.field("metadata_unsupported", &self.metadata_unsupported())
.finish()
}
}
pub struct Closed(pub(crate) CF_CALLBACK_PARAMETERS_0_1);
impl Closed {
pub fn deleted(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_CLOSE_COMPLETION_FLAG_DELETED).0 != 0
}
}
impl Debug for Closed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Closed")
.field("deleted", &self.deleted())
.finish()
}
}
pub struct Dehydrate(pub(crate) CF_CALLBACK_PARAMETERS_0_3);
impl Dehydrate {
pub fn background(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_DEHYDRATE_FLAG_BACKGROUND).0 != 0
}
pub fn reason(&self) -> Option<DehydrationReason> {
DehydrationReason::from_win32(self.0.Reason)
}
}
impl Debug for Dehydrate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Dehydrate")
.field("background", &self.background())
.field("reason", &self.reason())
.finish()
}
}
pub struct Dehydrated(pub(crate) CF_CALLBACK_PARAMETERS_0_2);
impl Dehydrated {
pub fn background(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_DEHYDRATE_COMPLETION_FLAG_BACKGROUND).0 != 0
}
pub fn already_hydrated(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_DEHYDRATE_COMPLETION_FLAG_DEHYDRATED).0 != 0
}
pub fn reason(&self) -> Option<DehydrationReason> {
DehydrationReason::from_win32(self.0.Reason)
}
}
impl Debug for Dehydrated {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Dehydrated")
.field("background", &self.background())
.field("already_hydrated", &self.already_hydrated())
.field("reason", &self.reason())
.finish()
}
}
pub struct Delete(pub(crate) CF_CALLBACK_PARAMETERS_0_5);
impl Delete {
pub fn is_directory(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_DELETE_FLAG_IS_DIRECTORY).0 != 0
}
pub fn is_undelete(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_DELETE_FLAG_IS_UNDELETE).0 != 0
}
}
impl Debug for Delete {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Delete")
.field("is_directory", &self.is_directory())
.field("is_undelete", &self.is_undelete())
.finish()
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct Deleted(pub(crate) CF_CALLBACK_PARAMETERS_0_4);
pub struct Rename(pub(crate) CF_CALLBACK_PARAMETERS_0_10, pub(crate) OsString);
impl Rename {
pub fn is_directory(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_RENAME_FLAG_IS_DIRECTORY).0 != 0
}
pub fn source_in_scope(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_RENAME_FLAG_SOURCE_IN_SCOPE).0 != 0
}
pub fn target_in_scope(&self) -> bool {
(self.0.Flags & CloudFilters::CF_CALLBACK_RENAME_FLAG_TARGET_IN_SCOPE).0 != 0
}
pub fn target_path(&self) -> PathBuf {
let mut path = PathBuf::from(&self.1);
path.push(unsafe { U16CStr::from_ptr_str(self.0.TargetPath.0) }.to_os_string());
path
}
}
impl Debug for Rename {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Rename")
.field("is_directory", &self.is_directory())
.field("source_in_scope", &self.source_in_scope())
.field("target_in_scope", &self.target_in_scope())
.field("target_path", &self.target_path())
.finish()
}
}
pub struct Renamed(pub(crate) CF_CALLBACK_PARAMETERS_0_9, pub(crate) OsString);
impl Renamed {
pub fn source_path(&self) -> PathBuf {
let mut path = PathBuf::from(&self.1);
path.push(unsafe { U16CStr::from_ptr_str(self.0.SourcePath.0) }.to_os_string());
path
}
}
impl Debug for Renamed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Renamed")
.field("source_path", &self.source_path())
.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DehydrationReason {
UserManually,
LowSpace,
Inactive,
OsUpgrade,
}
impl DehydrationReason {
fn from_win32(reason: CF_CALLBACK_DEHYDRATION_REASON) -> Option<DehydrationReason> {
match reason {
CloudFilters::CF_CALLBACK_DEHYDRATION_REASON_USER_MANUAL => Some(Self::UserManually),
CloudFilters::CF_CALLBACK_DEHYDRATION_REASON_SYSTEM_LOW_SPACE => Some(Self::LowSpace),
CloudFilters::CF_CALLBACK_DEHYDRATION_REASON_SYSTEM_INACTIVITY => Some(Self::Inactive),
CloudFilters::CF_CALLBACK_DEHYDRATION_REASON_SYSTEM_OS_UPGRADE => Some(Self::OsUpgrade),
_ => None,
}
}
}