1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! The `Filterer` trait for event filtering.
use std::{fmt, path::Path, sync::Arc};
use watchexec_events::{Event, Priority};
use crate::{changeable::Changeable, error::RuntimeError};
/// An interface for filtering events.
pub trait Filterer: std::fmt::Debug + Send + Sync {
/// Called while reconciling filesystem event sources to decide whether a directory should be
/// watched.
///
/// This is source filtering, which is separate from [`Filterer::check_event`]. Returning `false`
/// excludes the directory as an event source, including its descendants; `check_event` still
/// filters events which are observed from accepted sources. Implementations should therefore
/// reject a directory only when every event beneath it can safely be ignored.
///
/// An exact configured watch root is never passed to this method. The root remains an event
/// source even if this method would reject it, while its descendants are checked normally.
/// Watchexec only calls this method on filesystem backends for which it manages recursion; see
/// [`crate::sources::fs`] for the backend-specific behaviour.
///
/// Like event filtering, source-directory filtering is synchronous, should be fast, and must not
/// block the thread.
///
/// The default implementation accepts every directory.
fn check_dir(&self, _path: &Path) -> Result<bool, RuntimeError> {
Ok(true)
}
/// Called on (almost) every event, and should return `false` if the event is to be discarded.
///
/// Checking whether an event passes a filter is synchronous, should be fast, and must not block
/// the thread. Do any expensive stuff upfront during construction of your filterer, or in a
/// separate thread/task, as needed.
///
/// Returning an error will also fail the event processing, but the error will be propagated to
/// the watchexec error handler. While the type signature supports any [`RuntimeError`], it's
/// preferred that you create your own error type and return it wrapped in the
/// [`RuntimeError::Filterer`] variant with the name of your filterer as `kind`.
fn check_event(&self, event: &Event, priority: Priority) -> Result<bool, RuntimeError>;
}
impl Filterer for () {
fn check_dir(&self, _path: &Path) -> Result<bool, RuntimeError> {
Ok(true)
}
fn check_event(&self, _event: &Event, _priority: Priority) -> Result<bool, RuntimeError> {
Ok(true)
}
}
impl<T: Filterer + ?Sized> Filterer for Arc<T> {
fn check_dir(&self, path: &Path) -> Result<bool, RuntimeError> {
Self::as_ref(self).check_dir(path)
}
fn check_event(&self, event: &Event, priority: Priority) -> Result<bool, RuntimeError> {
Self::as_ref(self).check_event(event, priority)
}
}
/// A shareable `Filterer` that doesn't hold a lock when it is called.
///
/// This is a specialisation of [`Changeable`] for `Filterer`.
pub struct ChangeableFilterer(Changeable<Arc<dyn Filterer>>);
impl ChangeableFilterer {
/// Replace the filterer with a new one.
///
/// This type does not know whether it belongs to a [`Config`](crate::Config), so calling this on
/// `Config::filterer` does not emit the config change signal by itself. Prefer
/// [`Config::filterer`](crate::Config::filterer), or call
/// [`Config::signal_change`](crate::Config::signal_change) after direct replacement so filesystem
/// sources are reconciled.
///
/// Panics if the lock was poisoned.
pub fn replace(&self, new: impl Filterer + 'static) {
self.0.replace(Arc::new(new));
}
/// Get a stable snapshot of the current filterer.
///
/// The snapshot remains valid if the configured filterer is replaced, and its pointer identity
/// can be compared with later snapshots to detect a replacement.
#[must_use]
pub(crate) fn snapshot(&self) -> Arc<dyn Filterer> {
self.0.get()
}
}
impl Filterer for ChangeableFilterer {
fn check_dir(&self, path: &Path) -> Result<bool, RuntimeError> {
self.snapshot().check_dir(path)
}
fn check_event(&self, event: &Event, priority: Priority) -> Result<bool, RuntimeError> {
self.snapshot().check_event(event, priority)
}
}
// the derive adds a T: Clone bound
impl Clone for ChangeableFilterer {
fn clone(&self) -> Self {
Self(Changeable::clone(&self.0))
}
}
impl Default for ChangeableFilterer {
fn default() -> Self {
Self(Changeable::new(Arc::new(())))
}
}
impl fmt::Debug for ChangeableFilterer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ChangeableFilterer")
.field("filterer", &format!("{:?}", self.0.get()))
.finish_non_exhaustive()
}
}