|
6 | 6 |
|
7 | 7 | use super::event::*; |
8 | 8 | use super::{Config, Error, EventHandler, RecursiveMode, Result, Watcher}; |
9 | | -use crate::{unbounded, Receiver, Sender}; |
| 9 | +use crate::{unbounded, PathsMut, Receiver, Sender}; |
10 | 10 | use kqueue::{EventData, EventFilter, FilterFlag, Ident}; |
11 | 11 | use std::collections::HashMap; |
12 | 12 | use std::env; |
@@ -46,6 +46,7 @@ pub struct KqueueWatcher { |
46 | 46 |
|
47 | 47 | enum EventLoopMsg { |
48 | 48 | AddWatch(PathBuf, RecursiveMode, Sender<Result<()>>), |
| 49 | + AddWatchMultiple(Vec<(PathBuf, RecursiveMode)>, Sender<Result<()>>), |
49 | 50 | RemoveWatch(PathBuf, Sender<Result<()>>), |
50 | 51 | Shutdown, |
51 | 52 | } |
@@ -133,6 +134,9 @@ impl EventLoop { |
133 | 134 | EventLoopMsg::AddWatch(path, recursive_mode, tx) => { |
134 | 135 | let _ = tx.send(self.add_watch(path, recursive_mode.is_recursive())); |
135 | 136 | } |
| 137 | + EventLoopMsg::AddWatchMultiple(paths, tx) => { |
| 138 | + let _ = tx.send(self.add_watch_multiple(paths)); |
| 139 | + } |
136 | 140 | EventLoopMsg::RemoveWatch(path, tx) => { |
137 | 141 | let _ = tx.send(self.remove_watch(path, false)); |
138 | 142 | } |
@@ -313,6 +317,30 @@ impl EventLoop { |
313 | 317 | Ok(()) |
314 | 318 | } |
315 | 319 |
|
| 320 | + fn add_watch_multiple(&mut self, paths: Vec<(PathBuf, RecursiveMode)>) -> Result<()> { |
| 321 | + for (path, recursive_mode) in paths { |
| 322 | + let is_recursive = recursive_mode.is_recursive(); |
| 323 | + // If the watch is not recursive, or if we determine (by stat'ing the path to get its |
| 324 | + // metadata) that the watched path is not a directory, add a single path watch. |
| 325 | + if !is_recursive || !metadata(&path).map_err(Error::io)?.is_dir() { |
| 326 | + self.add_single_watch(path, false)?; |
| 327 | + } else { |
| 328 | + for entry in WalkDir::new(path) |
| 329 | + .follow_links(self.follow_symlinks) |
| 330 | + .into_iter() |
| 331 | + { |
| 332 | + let entry = entry.map_err(map_walkdir_error)?; |
| 333 | + self.add_single_watch(entry.into_path(), is_recursive)?; |
| 334 | + } |
| 335 | + } |
| 336 | + } |
| 337 | + |
| 338 | + // Only make a single `kevent` syscall to add all the watches. |
| 339 | + self.kqueue.watch()?; |
| 340 | + |
| 341 | + Ok(()) |
| 342 | + } |
| 343 | + |
316 | 344 | /// Adds a single watch to the kqueue. |
317 | 345 | /// |
318 | 346 | /// The caller of this function must call `self.kqueue.watch()` afterwards to register the new watch. |
@@ -374,6 +402,34 @@ fn map_walkdir_error(e: walkdir::Error) -> Error { |
374 | 402 | } |
375 | 403 | } |
376 | 404 |
|
| 405 | +struct KqueuePathsMut<'a> { |
| 406 | + inner: &'a mut KqueueWatcher, |
| 407 | + add_paths: Vec<(PathBuf, RecursiveMode)>, |
| 408 | +} |
| 409 | +impl<'a> KqueuePathsMut<'a> { |
| 410 | + fn new(watcher: &'a mut KqueueWatcher) -> Self { |
| 411 | + Self { |
| 412 | + inner: watcher, |
| 413 | + add_paths: Vec::new(), |
| 414 | + } |
| 415 | + } |
| 416 | +} |
| 417 | +impl PathsMut for KqueuePathsMut<'_> { |
| 418 | + fn add(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> { |
| 419 | + self.add_paths.push((path.to_owned(), recursive_mode)); |
| 420 | + Ok(()) |
| 421 | + } |
| 422 | + |
| 423 | + fn remove(&mut self, path: &Path) -> Result<()> { |
| 424 | + self.inner.unwatch_inner(path) |
| 425 | + } |
| 426 | + |
| 427 | + fn commit(self: Box<Self>) -> Result<()> { |
| 428 | + let paths = self.add_paths; |
| 429 | + self.inner.watch_multiple_inner(paths) |
| 430 | + } |
| 431 | +} |
| 432 | + |
377 | 433 | impl KqueueWatcher { |
378 | 434 | fn from_event_handler( |
379 | 435 | event_handler: Box<dyn EventHandler>, |
@@ -408,6 +464,32 @@ impl KqueueWatcher { |
408 | 464 | .map_err(|e| Error::generic(&e.to_string())) |
409 | 465 | } |
410 | 466 |
|
| 467 | + fn watch_multiple_inner(&mut self, paths: Vec<(PathBuf, RecursiveMode)>) -> Result<()> { |
| 468 | + let pbs = paths |
| 469 | + .into_iter() |
| 470 | + .map(|(path, recursive_mode)| { |
| 471 | + if path.is_absolute() { |
| 472 | + Ok((path, recursive_mode)) |
| 473 | + } else { |
| 474 | + let p = env::current_dir().map_err(Error::io)?; |
| 475 | + Ok((p.join(path), recursive_mode)) |
| 476 | + } |
| 477 | + }) |
| 478 | + .collect::<Result<Vec<(PathBuf, RecursiveMode)>>>()?; |
| 479 | + let (tx, rx) = unbounded(); |
| 480 | + let msg = EventLoopMsg::AddWatchMultiple(pbs, tx); |
| 481 | + |
| 482 | + self.channel |
| 483 | + .send(msg) |
| 484 | + .map_err(|e| Error::generic(&e.to_string()))?; |
| 485 | + self.waker |
| 486 | + .wake() |
| 487 | + .map_err(|e| Error::generic(&e.to_string()))?; |
| 488 | + rx.recv() |
| 489 | + .unwrap() |
| 490 | + .map_err(|e| Error::generic(&e.to_string())) |
| 491 | + } |
| 492 | + |
411 | 493 | fn unwatch_inner(&mut self, path: &Path) -> Result<()> { |
412 | 494 | let pb = if path.is_absolute() { |
413 | 495 | path.to_owned() |
@@ -440,6 +522,10 @@ impl Watcher for KqueueWatcher { |
440 | 522 | self.watch_inner(path, recursive_mode) |
441 | 523 | } |
442 | 524 |
|
| 525 | + fn paths_mut<'me>(&'me mut self) -> Box<dyn PathsMut + 'me> { |
| 526 | + Box::new(KqueuePathsMut::new(self)) |
| 527 | + } |
| 528 | + |
443 | 529 | fn unwatch(&mut self, path: &Path) -> Result<()> { |
444 | 530 | self.unwatch_inner(path) |
445 | 531 | } |
|
0 commit comments