Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions notify-debouncer-full/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ pub use notify_types::debouncer_full::DebouncedEvent;
use file_id::FileId;
use notify::{
event::{ModifyKind, RemoveKind, RenameMode},
Error, ErrorKind, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher, WatcherKind,
Error, ErrorKind, Event, EventKind, RecommendedWatcher, RecursiveMode, WatchFilter, Watcher,
WatcherKind,
};

/// The set of requirements for watcher debounce event handling functions.
Expand Down Expand Up @@ -580,7 +581,17 @@ impl<T: Watcher, C: FileIdCache> Debouncer<T, C> {
path: impl AsRef<Path>,
recursive_mode: RecursiveMode,
) -> notify::Result<()> {
self.watcher.watch(path.as_ref(), recursive_mode)?;
self.watch_filtered(path, recursive_mode, WatchFilter::accept_all())
}

pub fn watch_filtered(
&mut self,
path: impl AsRef<Path>,
recursive_mode: RecursiveMode,
watch_filter: WatchFilter,
) -> notify::Result<()> {
self.watcher
.watch_filtered(path.as_ref(), recursive_mode, watch_filter)?;
self.add_root(path.as_ref(), recursive_mode);
Ok(())
}
Expand Down
43 changes: 31 additions & 12 deletions notify/src/fsevent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
#![allow(non_upper_case_globals, dead_code)]

use crate::event::*;
use crate::{unbounded, Config, Error, EventHandler, RecursiveMode, Result, Sender, Watcher};
use crate::{
unbounded, Config, Error, EventHandler, RecursiveMode, Result, Sender, WatchFilter, Watcher,
};
use fsevent_sys as fs;
use fsevent_sys::core_foundation as cf;
use std::collections::HashMap;
Expand Down Expand Up @@ -66,7 +68,7 @@ pub struct FsEventWatcher {
flags: fs::FSEventStreamCreateFlags,
event_handler: Arc<Mutex<dyn EventHandler>>,
runloop: Option<(cf::CFRunLoopRef, thread::JoinHandle<()>)>,
recursive_info: HashMap<PathBuf, bool>,
recursive_info: HashMap<PathBuf, (bool, WatchFilter)>,
}

impl fmt::Debug for FsEventWatcher {
Expand Down Expand Up @@ -242,7 +244,7 @@ fn translate_flags(flags: StreamFlags, precise: bool) -> Vec<Event> {

struct StreamContextInfo {
event_handler: Arc<Mutex<dyn EventHandler>>,
recursive_info: HashMap<PathBuf, bool>,
recursive_info: HashMap<PathBuf, (bool, WatchFilter)>,
}

// Free the context when the stream created by `FSEventStreamCreate` is released.
Expand Down Expand Up @@ -280,9 +282,14 @@ impl FsEventWatcher {
})
}

fn watch_inner(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
fn watch_inner(
&mut self,
path: &Path,
recursive_mode: RecursiveMode,
watch_filter: WatchFilter,
) -> Result<()> {
self.stop();
let result = self.append_path(path, recursive_mode);
let result = self.append_path(path, recursive_mode, watch_filter);
// ignore return error: may be empty path list
let _ = self.run();
result
Expand Down Expand Up @@ -360,7 +367,12 @@ impl FsEventWatcher {
}

// https://github.com/thibaudgg/rb-fsevent/blob/master/ext/fsevent_watch/main.c
fn append_path(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
fn append_path(
&mut self,
path: &Path,
recursive_mode: RecursiveMode,
watch_filter: WatchFilter,
) -> Result<()> {
if !path.exists() {
return Err(Error::path_not_found().add_path(path.into()));
}
Expand All @@ -378,8 +390,10 @@ impl FsEventWatcher {
cf::CFArrayAppendValue(self.paths, cf_path);
cf::CFRelease(cf_path);
}
self.recursive_info
.insert(canonical_path, recursive_mode.is_recursive());
self.recursive_info.insert(
canonical_path,
(recursive_mode.is_recursive(), watch_filter),
);
Ok(())
}

Expand Down Expand Up @@ -522,8 +536,8 @@ unsafe fn callback_impl(
});

let mut handle_event = false;
for (p, r) in &(*info).recursive_info {
if path.starts_with(p) {
for (p, (r, filt)) in &(*info).recursive_info {
if path.starts_with(p) && filt.should_watch(p) {
if *r || &path == p {
handle_event = true;
break;
Expand Down Expand Up @@ -557,8 +571,13 @@ impl Watcher for FsEventWatcher {
Self::from_event_handler(Arc::new(Mutex::new(event_handler)))
}

fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
self.watch_inner(path, recursive_mode)
fn watch_filtered(
&mut self,
path: &Path,
recursive_mode: RecursiveMode,
watch_filter: WatchFilter,
) -> Result<()> {
self.watch_inner(path, recursive_mode, watch_filter)
}

fn unwatch(&mut self, path: &Path) -> Result<()> {
Expand Down
90 changes: 65 additions & 25 deletions notify/src/inotify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! will return events for the directory itself, and for files inside the directory.

use super::event::*;
use super::{Config, Error, ErrorKind, EventHandler, RecursiveMode, Result, Watcher};
use super::{Config, Error, ErrorKind, EventHandler, RecursiveMode, Result, WatchFilter, Watcher};
use crate::{bounded, unbounded, BoundSender, Receiver, Sender};
use inotify as inotify_sys;
use inotify_sys::{EventMask, Inotify, WatchDescriptor, WatchMask};
Expand Down Expand Up @@ -37,7 +37,7 @@ struct EventLoop {
inotify: Option<Inotify>,
event_handler: Box<dyn EventHandler>,
/// PathBuf -> (WatchDescriptor, WatchMask, is_recursive, is_dir)
watches: HashMap<PathBuf, (WatchDescriptor, WatchMask, bool, bool)>,
watches: HashMap<PathBuf, (WatchDescriptor, WatchMask, bool, bool, WatchFilter)>,
paths: HashMap<WatchDescriptor, PathBuf>,
rename_event: Option<Event>,
follow_links: bool,
Expand All @@ -51,7 +51,7 @@ pub struct INotifyWatcher {
}

enum EventLoopMsg {
AddWatch(PathBuf, RecursiveMode, Sender<Result<()>>),
AddWatch(PathBuf, RecursiveMode, WatchFilter, Sender<Result<()>>),
RemoveWatch(PathBuf, Sender<Result<()>>),
Shutdown,
Configure(Config, BoundSender<Result<bool>>),
Expand All @@ -61,15 +61,15 @@ enum EventLoopMsg {
fn add_watch_by_event(
path: &Option<PathBuf>,
event: &inotify_sys::Event<&OsStr>,
watches: &HashMap<PathBuf, (WatchDescriptor, WatchMask, bool, bool)>,
add_watches: &mut Vec<PathBuf>,
watches: &HashMap<PathBuf, (WatchDescriptor, WatchMask, bool, bool, WatchFilter)>,
add_watches: &mut Vec<(PathBuf, WatchFilter)>,
) {
if let Some(ref path) = *path {
if event.mask.contains(EventMask::ISDIR) {
if let Some(parent_path) = path.parent() {
if let Some(&(_, _, is_recursive, _)) = watches.get(parent_path) {
if let Some(&(_, _, is_recursive, _, ref filter)) = watches.get(parent_path) {
if is_recursive {
add_watches.push(path.to_owned());
add_watches.push((path.to_owned(), filter.clone()));
}
}
}
Expand All @@ -80,7 +80,7 @@ fn add_watch_by_event(
#[inline]
fn remove_watch_by_event(
path: &Option<PathBuf>,
watches: &HashMap<PathBuf, (WatchDescriptor, WatchMask, bool, bool)>,
watches: &HashMap<PathBuf, (WatchDescriptor, WatchMask, bool, bool, WatchFilter)>,
remove_watches: &mut Vec<PathBuf>,
) {
if let Some(ref path) = *path {
Expand Down Expand Up @@ -172,8 +172,13 @@ impl EventLoop {
fn handle_messages(&mut self) {
while let Ok(msg) = self.event_loop_rx.try_recv() {
match msg {
EventLoopMsg::AddWatch(path, recursive_mode, tx) => {
let _ = tx.send(self.add_watch(path, recursive_mode.is_recursive(), true));
EventLoopMsg::AddWatch(path, recursive_mode, watch_filter, tx) => {
let _ = tx.send(self.add_watch(
path,
recursive_mode.is_recursive(),
true,
watch_filter,
));
}
EventLoopMsg::RemoveWatch(path, tx) => {
let _ = tx.send(self.remove_watch(path, false));
Expand Down Expand Up @@ -307,8 +312,8 @@ impl EventLoop {
Some(watched_path) => {
let current_watch = self.watches.get(watched_path);
match current_watch {
Some(&(_, _, _, true)) => RemoveKind::Folder,
Some(&(_, _, _, false)) => RemoveKind::File,
Some(&(_, _, _, true, _)) => RemoveKind::Folder,
Some(&(_, _, _, false, _)) => RemoveKind::File,
None => RemoveKind::Other,
}
}
Expand Down Expand Up @@ -391,24 +396,40 @@ impl EventLoop {
self.remove_watch(path, true).ok();
}

for path in add_watches {
self.add_watch(path, true, false).ok();
for (path, filter) in add_watches {
self.add_watch(path, true, false, filter).ok();
}
}

fn add_watch(&mut self, path: PathBuf, is_recursive: bool, mut watch_self: bool) -> Result<()> {
fn add_watch(
&mut self,
path: PathBuf,
is_recursive: bool,
mut watch_self: bool,
watch_filter: WatchFilter,
) -> Result<()> {
if !watch_filter.should_watch(&path) {
return Ok(());
}

// If the watch is not recursive, or if we determine (by stat'ing the path to get its
// metadata) that the watched path is not a directory, add a single path watch.
if !is_recursive || !metadata(&path).map_err(Error::io_watch)?.is_dir() {
return self.add_single_watch(path, false, true);
return self.add_single_watch(path, false, true, WatchFilter::accept_all());
}

for entry in WalkDir::new(path)
.follow_links(self.follow_links)
.into_iter()
.filter_map(filter_dir)
.filter(|e| watch_filter.should_watch(e.path()))
{
self.add_single_watch(entry.path().to_path_buf(), is_recursive, watch_self)?;
self.add_single_watch(
entry.path().to_path_buf(),
is_recursive,
watch_self,
watch_filter.clone(),
)?;
watch_self = false;
}

Expand All @@ -420,6 +441,7 @@ impl EventLoop {
path: PathBuf,
is_recursive: bool,
watch_self: bool,
watch_filter: WatchFilter,
) -> Result<()> {
let mut watchmask = WatchMask::ATTRIB
| WatchMask::CREATE
Expand All @@ -435,7 +457,7 @@ impl EventLoop {
watchmask.insert(WatchMask::MOVE_SELF);
}

if let Some(&(_, old_watchmask, _, _)) = self.watches.get(&path) {
if let Some(&(_, old_watchmask, _, _, _)) = self.watches.get(&path) {
watchmask.insert(old_watchmask);
watchmask.insert(WatchMask::MASK_ADD);
}
Expand All @@ -456,8 +478,16 @@ impl EventLoop {
Ok(w) => {
watchmask.remove(WatchMask::MASK_ADD);
let is_dir = metadata(&path).map_err(Error::io)?.is_dir();
self.watches
.insert(path.clone(), (w.clone(), watchmask, is_recursive, is_dir));
self.watches.insert(
path.clone(),
(
w.clone(),
watchmask,
is_recursive,
is_dir,
watch_filter.clone(),
),
);
self.paths.insert(w, path);
Ok(())
}
Expand All @@ -470,7 +500,7 @@ impl EventLoop {
fn remove_watch(&mut self, path: PathBuf, remove_recursive: bool) -> Result<()> {
match self.watches.remove(&path) {
None => return Err(Error::watch_not_found().add_path(path)),
Some((w, _, is_recursive, _)) => {
Some((w, _, is_recursive, _, _)) => {
if let Some(ref mut inotify) = self.inotify {
let mut inotify_watches = inotify.watches();
log::trace!("removing inotify watch: {}", path.display());
Expand Down Expand Up @@ -541,15 +571,20 @@ impl INotifyWatcher {
Ok(INotifyWatcher { channel, waker })
}

fn watch_inner(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
fn watch_inner(
&mut self,
path: &Path,
recursive_mode: RecursiveMode,
watch_filter: WatchFilter,
) -> Result<()> {
let pb = if path.is_absolute() {
path.to_owned()
} else {
let p = env::current_dir().map_err(Error::io)?;
p.join(path)
};
let (tx, rx) = unbounded();
let msg = EventLoopMsg::AddWatch(pb, recursive_mode, tx);
let msg = EventLoopMsg::AddWatch(pb, recursive_mode, watch_filter, tx);

// we expect the event loop to live and reply => unwraps must not panic
self.channel.send(msg).unwrap();
Expand Down Expand Up @@ -580,8 +615,13 @@ impl Watcher for INotifyWatcher {
Self::from_event_handler(Box::new(event_handler), config.follow_symlinks())
}

fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
self.watch_inner(path, recursive_mode)
fn watch_filtered(
&mut self,
path: &Path,
recursive_mode: RecursiveMode,
watch_filter: WatchFilter,
) -> Result<()> {
self.watch_inner(path, recursive_mode, watch_filter)
}

fn unwatch(&mut self, path: &Path) -> Result<()> {
Expand Down
9 changes: 7 additions & 2 deletions notify/src/kqueue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use super::event::*;
use super::{Config, Error, EventHandler, RecursiveMode, Result, Watcher};
use crate::{unbounded, Receiver, Sender};
use crate::{unbounded, Receiver, Sender, WatchFilter};
use kqueue::{EventData, EventFilter, FilterFlag, Ident};
use std::collections::HashMap;
use std::env;
Expand Down Expand Up @@ -435,7 +435,12 @@ impl Watcher for KqueueWatcher {
Self::from_event_handler(Box::new(event_handler), config.follow_symlinks())
}

fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
fn watch_filtered(
&mut self,
path: &Path,
recursive_mode: RecursiveMode,
_watch_filter: WatchFilter,
) -> Result<()> {
self.watch_inner(path, recursive_mode)
}

Expand Down
Loading