#![allow(missing_docs)]
mod timer;
use super::{op, DebouncedEvent, RawEvent};
use self::timer::WatchTimer;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::time::Duration;
pub type OperationsBufferInner = HashMap<PathBuf, (Option<op::Op>, Option<PathBuf>, Option<u64>)>;
pub type OperationsBuffer = Arc<Mutex<OperationsBufferInner>>;
pub enum EventTx {
Raw {
tx: mpsc::Sender<RawEvent>,
},
Debounced {
tx: mpsc::Sender<DebouncedEvent>,
debounce: Debounce,
},
DebouncedTx {
tx: mpsc::Sender<DebouncedEvent>,
},
}
impl EventTx {
pub fn send(&mut self, event: RawEvent) {
match *self {
EventTx::Raw { ref tx } => {
let _ = tx.send(event);
}
EventTx::Debounced {
ref tx,
ref mut debounce,
} => {
match (event.path, event.op, event.cookie) {
(None, Ok(op::Op::RESCAN), None) => {
let _ = tx.send(DebouncedEvent::Rescan);
}
(Some(path), Ok(op), cookie) => {
debounce.event(path, op, cookie);
}
(None, Ok(_op), _cookie) => {
}
(path, Err(e), _) => {
let _ = tx.send(DebouncedEvent::Error(e, path));
}
}
}
EventTx::DebouncedTx { ref tx } => {
match (event.path, event.op, event.cookie) {
(None, Ok(op::Op::RESCAN), None) => {
let _ = tx.send(DebouncedEvent::Rescan);
}
(Some(_path), Ok(_op), _cookie) => {
}
(None, Ok(_op), _cookie) => {
}
(path, Err(e), _) => {
let _ = tx.send(DebouncedEvent::Error(e, path));
}
}
}
}
}
}
pub struct Debounce {
tx: mpsc::Sender<DebouncedEvent>,
operations_buffer: OperationsBuffer,
rename_path: Option<PathBuf>,
rename_cookie: Option<u32>,
timer: WatchTimer,
}
impl Debounce {
pub fn new(delay: Duration, tx: mpsc::Sender<DebouncedEvent>) -> Debounce {
let operations_buffer: OperationsBuffer = Arc::new(Mutex::new(HashMap::new()));
let timer = WatchTimer::new(tx.clone(), operations_buffer.clone(), delay);
Debounce {
tx,
operations_buffer: operations_buffer,
rename_path: None,
rename_cookie: None,
timer,
}
}
fn check_partial_rename(&mut self, path: PathBuf, op: op::Op, cookie: Option<u32>) {
if let Ok(mut op_buf) = self.operations_buffer.lock() {
let mut remove_path: Option<PathBuf> = None;
if let Some(&mut (ref mut operation, ref mut from_path, ref mut timer_id)) =
op_buf.get_mut(self.rename_path.as_ref().unwrap())
{
if op != op::Op::RENAME
|| self.rename_cookie.is_none()
|| self.rename_cookie != cookie
{
if self.rename_path.as_ref().unwrap().exists() {
match *operation {
Some(op::Op::RENAME) if from_path.is_none() => {
*operation = Some(op::Op::CREATE);
restart_timer(timer_id, path, &mut self.timer);
}
Some(op::Op::REMOVE) => {
*operation = Some(op::Op::WRITE);
restart_timer(timer_id, path, &mut self.timer);
}
_ => {
}
}
} else {
match *operation {
Some(op::Op::CREATE) => {
if let Some(timer_id) = *timer_id {
self.timer.ignore(timer_id);
}
remove_path = Some(path);
}
Some(op::Op::WRITE) |
Some(op::Op::CHMOD) => {
*operation = Some(op::Op::REMOVE);
let _ = self.tx.send(DebouncedEvent::NoticeRemove(path.clone()));
restart_timer(timer_id, path, &mut self.timer);
}
Some(op::Op::RENAME) => {
*operation = Some(op::Op::REMOVE);
restart_timer(timer_id, path, &mut self.timer);
}
Some(op::Op::REMOVE) => {
restart_timer(timer_id, path, &mut self.timer);
}
_ => {
unreachable!();
}
}
}
self.rename_path = None;
}
}
if let Some(path) = remove_path {
op_buf.remove(&path);
}
}
}
pub fn event(&mut self, path: PathBuf, mut op: op::Op, cookie: Option<u32>) {
if op.contains(op::Op::RESCAN) {
let _ = self.tx.send(DebouncedEvent::Rescan);
}
if self.rename_path.is_some() {
self.check_partial_rename(path.clone(), op, cookie);
}
if let Ok(mut op_buf) = self.operations_buffer.lock() {
if let Some(&(operation, _, _)) = op_buf.get(&path) {
op = remove_repeated_events(op, operation);
} else if op.contains(op::Op::CREATE | op::Op::REMOVE) {
if path.exists() {
op.remove(op::Op::REMOVE);
} else {
op.remove(op::Op::CREATE);
}
}
if op.contains(op::Op::CREATE) {
let &mut (ref mut operation, _, ref mut timer_id) =
op_buf.entry(path.clone()).or_insert((None, None, None));
match *operation {
Some(op::Op::CREATE) |
Some(op::Op::WRITE) |
Some(op::Op::CHMOD) |
Some(op::Op::RENAME) => {}
Some(op::Op::REMOVE) => {
*operation = Some(op::Op::WRITE);
restart_timer(timer_id, path.clone(), &mut self.timer);
}
None => {
*operation = Some(op::Op::CREATE);
restart_timer(timer_id, path.clone(), &mut self.timer);
}
_ => { unreachable!(); }
}
}
if op.contains(op::Op::WRITE) {
let &mut (ref mut operation, _, ref mut timer_id) =
op_buf.entry(path.clone()).or_insert((None, None, None));
match *operation {
Some(op::Op::CREATE) |
Some(op::Op::WRITE) => {
restart_timer(timer_id, path.clone(), &mut self.timer);
}
Some(op::Op::CHMOD) |
Some(op::Op::RENAME) |
None => {
*operation = Some(op::Op::WRITE);
let _ = self.tx.send(DebouncedEvent::NoticeWrite(path.clone()));
restart_timer(timer_id, path.clone(), &mut self.timer);
}
Some(op::Op::REMOVE) => {}
_ => { unreachable!(); }
}
}
if op.contains(op::Op::CHMOD) {
let &mut (ref mut operation, _, ref mut timer_id) =
op_buf.entry(path.clone()).or_insert((None, None, None));
match *operation {
Some(op::Op::CREATE) |
Some(op::Op::WRITE) |
Some(op::Op::CHMOD) => { restart_timer(timer_id, path.clone(), &mut self.timer); }
Some(op::Op::RENAME) |
None => {
*operation = Some(op::Op::CHMOD);
restart_timer(timer_id, path.clone(), &mut self.timer);
}
Some(op::Op::REMOVE) => {}
_ => { unreachable!(); }
}
}
if op.contains(op::Op::RENAME) {
if self.rename_path.is_some()
&& self.rename_cookie.is_some()
&& self.rename_cookie == cookie
&& op_buf.contains_key(self.rename_path.as_ref().unwrap())
{
let (from_operation, from_from_path, from_timer_id) =
op_buf.remove(self.rename_path.as_ref().unwrap()).unwrap();
if let Some(from_timer_id) = from_timer_id {
self.timer.ignore(from_timer_id);
}
let use_from_path = from_from_path.or_else(|| self.rename_path.clone());
let &mut (ref mut operation, ref mut from_path, ref mut timer_id) =
op_buf.entry(path.clone()).or_insert((None, None, None));
match from_operation {
Some(op::Op::CREATE) => {
*operation = from_operation;
*from_path = None;
restart_timer(timer_id, path.clone(), &mut self.timer);
}
Some(op::Op::WRITE) |
Some(op::Op::CHMOD) |
Some(op::Op::RENAME) => {
*operation = from_operation;
*from_path = use_from_path;
restart_timer(timer_id, path.clone(), &mut self.timer);
}
Some(op::Op::REMOVE) => {}
_ => { unreachable!(); }
}
self.rename_path = None;
} else {
self.rename_path = Some(path.clone());
self.rename_cookie = cookie;
let &mut (ref mut operation, _, ref mut timer_id) =
op_buf.entry(path.clone()).or_insert((None, None, None));
match *operation {
Some(op::Op::CREATE) |
Some(op::Op::RENAME) => {
restart_timer(timer_id, path.clone(), &mut self.timer);
}
Some(op::Op::WRITE) |
Some(op::Op::CHMOD) => {
let _ = self.tx.send(DebouncedEvent::NoticeRemove(path.clone()));
restart_timer(timer_id, path.clone(), &mut self.timer);
}
None => {
*operation = Some(op::Op::RENAME);
let _ = self.tx.send(DebouncedEvent::NoticeRemove(path.clone()));
restart_timer(timer_id, path.clone(), &mut self.timer);
}
Some(op::Op::REMOVE) => {}
_ => { unreachable!(); }
}
}
}
if op.contains(op::Op::REMOVE) {
let mut remove_path: Option<PathBuf> = None;
{
if let Some(&(_, ref from_path, ref timer_id)) = op_buf.get(&path) {
if let Some(ref from_path) = *from_path {
if op_buf.contains_key(from_path) {
if let Some(timer_id) = *timer_id {
self.timer.ignore(timer_id);
}
remove_path = Some(path.clone());
}
}
}
let &mut (ref mut operation, _, ref mut timer_id) =
op_buf.entry(path.clone()).or_insert((None, None, None));
if remove_path.is_none() {
match *operation {
Some(op::Op::CREATE) => {
if let Some(timer_id) = *timer_id {
self.timer.ignore(timer_id);
}
remove_path = Some(path);
}
Some(op::Op::WRITE) |
Some(op::Op::CHMOD) |
None => {
*operation = Some(op::Op::REMOVE);
let _ = self.tx.send(DebouncedEvent::NoticeRemove(path.clone()));
restart_timer(timer_id, path, &mut self.timer);
}
Some(op::Op::RENAME) => {
*operation = Some(op::Op::REMOVE);
restart_timer(timer_id, path, &mut self.timer);
}
Some(op::Op::REMOVE) => {}
_ => { unreachable!(); }
}
}
}
if let Some(path) = remove_path {
op_buf.remove(&path);
if self.rename_path == Some(path) {
self.rename_path = None;
}
}
}
}
}
}
fn remove_repeated_events(mut op: op::Op, prev_op: Option<op::Op>) -> op::Op {
if let Some(prev_op) = prev_op {
if prev_op.intersects(op::Op::CREATE | op::Op::WRITE | op::Op::CHMOD | op::Op::RENAME) {
op.remove(op::Op::CREATE);
}
if prev_op.contains(op::Op::REMOVE) {
op.remove(op::Op::REMOVE);
}
if prev_op.contains(op::Op::RENAME) && op & !op::Op::RENAME != op::Op::empty() {
op.remove(op::Op::RENAME);
}
}
op
}
fn restart_timer(timer_id: &mut Option<u64>, path: PathBuf, timer: &mut WatchTimer) {
if let Some(timer_id) = *timer_id {
timer.ignore(timer_id);
}
*timer_id = Some(timer.schedule(path));
}