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}