use std::borrow::Cow;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::task::{self, Poll};
use std::{fmt, io};
use crate::sys::fs_notify::{self as sys, EventsState, Watching, watch, watch_recursive};
use crate::{AsyncFd, SubmissionQueue, new_flag};
#[derive(Debug)]
pub struct Watcher {
pub(crate) fd: AsyncFd,
pub(crate) watching: Watching,
}
impl Watcher {
pub fn new(sq: SubmissionQueue) -> io::Result<Watcher> {
Watcher::new_sys(sq)
}
pub fn watch_directory(
&mut self,
dir: PathBuf,
interest: Interest,
recursive: Recursive,
) -> io::Result<()> {
watch_recursive(&self.fd, &mut self.watching, dir, interest, recursive, true)
}
pub fn watch_file(&mut self, file: PathBuf, interest: Interest) -> io::Result<()> {
watch(&self.fd, &mut self.watching, file, interest)
}
pub fn watch(
&mut self,
path: PathBuf,
interest: Interest,
recursive: Recursive,
) -> io::Result<()> {
watch_recursive(
&self.fd,
&mut self.watching,
path,
interest,
recursive,
false,
)
}
pub fn events<'w>(&'w mut self) -> Events<'w> {
Events {
fd: &self.fd,
watching: &mut self.watching,
state: EventsState::new(&self.fd),
}
}
}
new_flag!(
pub struct Interest(u32) impl BitOr {
ALL = sys::INTEREST_ALL,
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "netbsd"))]
ACCESS = sys::INTEREST_ACCESS,
MODIFY = sys::INTEREST_MODIFY,
METADATA = sys::INTEREST_METADATA,
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "netbsd"))]
CLOSE_WRITE = sys::INTEREST_CLOSE_WRITE,
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "netbsd"))]
CLOSE_NOWRITE = sys::INTEREST_CLOSE_NOWRITE,
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "netbsd"))]
CLOSE = sys::INTEREST_CLOSE,
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "netbsd"))]
OPEN = sys::INTEREST_OPEN,
MOVE_FROM = sys::INTEREST_MOVE_FROM,
MOVE_INTO = sys::INTEREST_MOVE_INTO,
MOVE = sys::INTEREST_MOVE,
CREATE = sys::INTEREST_CREATE,
DELETE = sys::INTEREST_DELETE,
DELETE_SELF = sys::INTEREST_DELETE_SELF,
MOVE_SELF = sys::INTEREST_MOVE_SELF,
}
);
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub enum Recursive {
No,
All,
}
#[must_use = "`AsyncIterator`s do nothing unless polled"]
#[derive(Debug)]
pub struct Events<'w> {
pub(crate) fd: &'w AsyncFd,
pub(crate) watching: &'w mut Watching,
pub(crate) state: EventsState<'w>,
}
impl<'w> Events<'w> {
pub fn path_for<'a>(&'a self, event: &'a Event) -> Cow<'a, Path> {
self.path_for_sys(&event.0)
}
pub fn watch_directory(
&mut self,
dir: PathBuf,
interest: Interest,
recursive: Recursive,
) -> io::Result<()> {
watch_recursive(self.fd, self.watching, dir, interest, recursive, true)
}
pub fn watch_file(&mut self, file: PathBuf, interest: Interest) -> io::Result<()> {
watch(self.fd, self.watching, file, interest)
}
pub fn watch(
&mut self,
path: PathBuf,
interest: Interest,
recursive: Recursive,
) -> io::Result<()> {
watch_recursive(self.fd, self.watching, path, interest, recursive, false)
}
pub fn poll_next(
self: Pin<&mut Self>,
ctx: &mut task::Context<'_>,
) -> Poll<Option<io::Result<&'w Event>>> {
self.poll_sys(ctx)
}
}
#[cfg(feature = "nightly")]
impl<'w> std::async_iter::AsyncIterator for Events<'w> {
type Item = io::Result<&'w Event>;
fn poll_next(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> {
self.poll_next(ctx)
}
}
#[repr(transparent)]
pub struct Event(sys::Event);
impl Event {
#[deprecated(
note = "this doesn't work for the kqueue implementation, use `Events::path_for` instead"
)]
pub fn file_path(&self) -> &Path {
self.0.file_path()
}
bit_checks!(
is_dir;
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "netbsd"))]
accessed, sys::EVENT_ACCESSED;
modified;
metadata_changed, sys::EVENT_METADATA_CHANGED;
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "netbsd"))]
closed_write, sys::EVENT_CLOSED_WRITE;
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "netbsd"))]
closed_no_write, sys::EVENT_CLOSED_NO_WRITE;
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "netbsd"))]
closed, sys::EVENT_CLOSED;
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux", target_os = "netbsd"))]
opened, sys::EVENT_OPENED;
deleted;
moved, sys::EVENT_MOVED;
unmounted, sys::EVENT_UNMOUNTED;
file_moved_from;
file_moved_into;
file_moved;
file_created;
file_deleted;
);
const fn mask(&self) -> u32 {
self.0.mask()
}
}
macro_rules! bit_checks {
( $( $(#[$meta: meta])* $fn_name: ident $(, $libc: ident :: $bit: ident)? ; )* ) => {
$(
$crate::fs::notify::bit_checks!(__fn $( #[$meta] )* $fn_name $(, $libc :: $bit )?);
)*
fn events(&self) -> impl fmt::Debug {
struct Events<'a>(&'a Event);
impl<'a> fmt::Debug for Events<'a> {
#[allow(unused_doc_comments)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_list();
$(
$( #[$meta] )*
if self.0.$fn_name() {
_ = f.entry(&stringify!($fn_name));
}
)*
f.finish()
}
}
Events(self)
}
};
(__fn $(#[$meta: meta])* $fn_name: ident) => {
$( #[$meta] )*
pub fn $fn_name(&self) -> bool {
self.0.$fn_name()
}
};
(__fn $(#[$meta: meta])* $fn_name: ident, $libc: ident :: $bit: ident ) => {
$( #[$meta] )*
pub fn $fn_name(&self) -> bool {
self.mask() & $libc::$bit != 0
}
};
}
use bit_checks;
#[allow(clippy::missing_fields_in_debug)]
impl fmt::Debug for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("Event");
self.0.fmt(&mut f).field("events", &self.events()).finish()
}
}