calloop_notify/
lib.rs

1//! Watch files using [`calloop`].
2//!
3//! This crate provides a wrapper around the [`notify`] crate to allow
4//! integration with the [`calloop`] event loop. To get started, just create a
5//! [`NotifySource`] and insert it into your event loop.
6//!
7//! ## Example
8//!
9//! ```rust,no_run
10//! use std::path::Path;
11//!
12//! use calloop::EventLoop;
13//! use calloop_notify::notify::{RecursiveMode, Watcher};
14//! use calloop_notify::NotifySource;
15//!
16//! // Create calloop event loop.
17//! let mut event_loop = EventLoop::try_new().unwrap();
18//! let loop_handle = event_loop.handle();
19//!
20//! // Watch current directory recursively.
21//! let mut notify_source = NotifySource::new().unwrap();
22//! notify_source.watch(Path::new("."), RecursiveMode::Recursive).unwrap();
23//!
24//! // Insert notify source into calloop.
25//! loop_handle
26//!     .insert_source(notify_source, |event, _, _: &mut ()| {
27//!         println!("Notify Event: {event:?}");
28//!     })
29//!     .unwrap();
30//!
31//! // Dispatch event loop.
32//! loop {
33//!     event_loop.dispatch(None, &mut ()).unwrap();
34//! }
35
36use std::ops::{Deref, DerefMut};
37
38use calloop::channel::{self, Channel, ChannelError, Event as ChannelEvent};
39use calloop::{EventSource, Poll, PostAction, Readiness, Token, TokenFactory};
40pub use notify;
41use notify::{Event, RecommendedWatcher};
42
43/// Calloop event source for watching files.
44///
45/// This struct implements [`EventSource`], which allows passing it to
46/// [`LoopHandle::insert_source`] for registering it with calloop.
47///
48/// Since this implements [`Deref`] and [`DerefMut`] into
49/// [`RecommendedWatcher`], you can call any function from [`Watcher`]
50/// on it. Use this to [`watch`] new files.
51///
52/// [`Watcher`]: notify::Watcher
53/// [`watch`]: notify::Watcher::watch
54///
55/// ## Example
56///
57/// ```rust,no_run
58/// use std::path::Path;
59///
60/// use calloop::EventLoop;
61/// use calloop_notify::NotifySource;
62/// use calloop_notify::notify::{RecursiveMode, Watcher};
63///
64/// // Create calloop event loop.
65/// let mut event_loop = EventLoop::try_new().unwrap();
66/// let loop_handle = event_loop.handle();
67///
68/// // Watch current directory recursively.
69/// let mut notify_source = NotifySource::new().unwrap();
70/// notify_source.watch(Path::new("."), RecursiveMode::Recursive).unwrap();
71///
72/// // Insert notify source into calloop.
73/// loop_handle
74///     .insert_source(notify_source, |event, _, _: &mut ()| {
75///         println!("Notify Event: {event:?}");
76///     })
77///     .unwrap();
78///
79/// // Dispatch event loop.
80/// loop {
81///     event_loop.dispatch(None, &mut ()).unwrap();
82/// }
83/// ```
84///
85/// [`LoopHandle::insert_source`]: calloop::LoopHandle::insert_source
86pub struct NotifySource {
87    watcher: RecommendedWatcher,
88    rx: Channel<Event>,
89}
90
91impl NotifySource {
92    /// Create the event source.
93    ///
94    /// ## Errors
95    ///
96    /// If the [`notify`] watcher creation failed.
97    ///
98    /// ## Example
99    ///
100    /// ```rust,no_run
101    /// use calloop_notify::NotifySource;
102    ///
103    /// let notify_source = NotifySource::new().unwrap();
104    /// ```
105    pub fn new() -> Result<Self, notify::Error> {
106        let (tx, rx) = channel::channel();
107
108        let watcher = notify::recommended_watcher(move |event| {
109            if let Ok(event) = event {
110                let _ = tx.send(event);
111            }
112        })?;
113
114        Ok(Self { watcher, rx })
115    }
116}
117
118impl Deref for NotifySource {
119    type Target = RecommendedWatcher;
120
121    fn deref(&self) -> &Self::Target {
122        &self.watcher
123    }
124}
125
126impl DerefMut for NotifySource {
127    fn deref_mut(&mut self) -> &mut Self::Target {
128        &mut self.watcher
129    }
130}
131
132impl EventSource for NotifySource {
133    type Error = ChannelError;
134    type Event = Event;
135    type Metadata = ();
136    type Ret = ();
137
138    fn process_events<F>(
139        &mut self,
140        readiness: Readiness,
141        token: Token,
142        mut callback: F,
143    ) -> Result<PostAction, Self::Error>
144    where
145        F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
146    {
147        self.rx.process_events(readiness, token, |event, metadata| match event {
148            ChannelEvent::Msg(msg) => callback(msg, metadata),
149            ChannelEvent::Closed => (),
150        })
151    }
152
153    fn register(
154        &mut self,
155        poll: &mut Poll,
156        token_factory: &mut TokenFactory,
157    ) -> calloop::Result<()> {
158        self.rx.register(poll, token_factory)
159    }
160
161    fn reregister(
162        &mut self,
163        poll: &mut Poll,
164        token_factory: &mut TokenFactory,
165    ) -> calloop::Result<()> {
166        self.rx.reregister(poll, token_factory)
167    }
168
169    fn unregister(&mut self, poll: &mut Poll) -> calloop::Result<()> {
170        self.rx.unregister(poll)
171    }
172}