use std::ffi::c_void;
use std::fmt;
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::Storage::FileSystem::{
FILE_NOTIFY_CHANGE, FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_NOTIFY_CHANGE_CREATION,
FILE_NOTIFY_CHANGE_DIR_NAME, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_NOTIFY_CHANGE_LAST_ACCESS,
FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_NOTIFY_CHANGE_SECURITY, FILE_NOTIFY_CHANGE_SIZE,
FindCloseChangeNotification, FindFirstChangeNotificationW, FindNextChangeNotification,
};
use crate::outcome::{Outcome, perform_bool, perform_handle};
use crate::path::PreparedPath;
#[derive(Debug)]
#[must_use = "dropping the notification stops the watch"]
pub struct ChangeNotification {
handle: HANDLE,
}
impl ChangeNotification {
#[must_use]
pub fn as_raw(&self) -> HANDLE {
self.handle
}
pub fn rearm(&self) -> Outcome<()> {
perform_bool(|| unsafe { FindNextChangeNotification(self.handle) })
}
}
impl Drop for ChangeNotification {
fn drop(&mut self) {
unsafe {
FindCloseChangeNotification(self.handle);
}
}
}
unsafe impl Send for ChangeNotification {}
unsafe impl Sync for ChangeNotification {}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct NotifyFilter(FILE_NOTIFY_CHANGE);
impl NotifyFilter {
pub const NONE: Self = Self(0);
pub const FILE_NAME: Self = Self(FILE_NOTIFY_CHANGE_FILE_NAME);
pub const DIR_NAME: Self = Self(FILE_NOTIFY_CHANGE_DIR_NAME);
pub const ATTRIBUTES: Self = Self(FILE_NOTIFY_CHANGE_ATTRIBUTES);
pub const SIZE: Self = Self(FILE_NOTIFY_CHANGE_SIZE);
pub const LAST_WRITE: Self = Self(FILE_NOTIFY_CHANGE_LAST_WRITE);
pub const LAST_ACCESS: Self = Self(FILE_NOTIFY_CHANGE_LAST_ACCESS);
pub const CREATION: Self = Self(FILE_NOTIFY_CHANGE_CREATION);
pub const SECURITY: Self = Self(FILE_NOTIFY_CHANGE_SECURITY);
#[must_use]
pub const fn from_bits(bits: FILE_NOTIFY_CHANGE) -> Self {
Self(bits)
}
#[must_use]
pub const fn bits(self) -> FILE_NOTIFY_CHANGE {
self.0
}
#[must_use]
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
#[must_use]
pub const fn contains(self, other: Self) -> bool {
self.0 & other.0 == other.0
}
}
impl std::ops::BitOr for NotifyFilter {
type Output = Self;
fn bitor(self, other: Self) -> Self {
self.union(other)
}
}
#[derive(Clone, Debug)]
#[must_use = "an unperformed request watches nothing"]
pub struct WatchDirectory {
path: PreparedPath,
subtree: bool,
filter: NotifyFilter,
}
impl WatchDirectory {
pub fn new(path: PreparedPath) -> Self {
Self {
path,
subtree: false,
filter: NotifyFilter::NONE,
}
}
pub fn with_subtree(mut self, subtree: bool) -> Self {
self.subtree = subtree;
self
}
pub fn with_filter(mut self, filter: NotifyFilter) -> Self {
self.filter = filter;
self
}
#[must_use]
pub fn path(&self) -> &PreparedPath {
&self.path
}
#[must_use]
pub fn subtree(&self) -> bool {
self.subtree
}
#[must_use]
pub fn filter(&self) -> NotifyFilter {
self.filter
}
pub fn perform(&self) -> Outcome<ChangeNotification> {
let raw = perform_handle(|| {
unsafe {
FindFirstChangeNotificationW(
self.path.as_wtf16_terminated(),
i32::from(self.subtree),
self.filter.bits(),
)
}
})?;
Ok(ChangeNotification {
handle: raw.cast::<c_void>(),
})
}
}
impl fmt::Display for NotifyFilter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FILE_NOTIFY_CHANGE({:#x})", self.0)
}
}
impl crate::request::Request for WatchDirectory {
type Error = crate::Win32Error;
type Output = ChangeNotification;
fn perform(&self) -> Outcome<ChangeNotification> {
Self::perform(self)
}
}
#[cfg(test)]
mod tests;