Skip to main content

notify_types/
event.rs

1//! The `Event` type and the hierarchical `EventKind` descriptor.
2
3use std::{
4    fmt,
5    hash::{Hash, Hasher},
6    path::PathBuf,
7};
8
9use bitflags::bitflags;
10
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14/// An event describing open or close operations on files.
15#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
16#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
17#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
18pub enum AccessMode {
19    /// The catch-all case, to be used when the specific kind of event is unknown.
20    Any,
21
22    /// An event emitted when the file is executed, or the folder opened.
23    Execute,
24
25    /// An event emitted when the file is opened for reading.
26    Read,
27
28    /// An event emitted when the file is opened for writing.
29    Write,
30
31    /// An event which specific kind is known but cannot be represented otherwise.
32    Other,
33}
34
35/// An event describing non-mutating access operations on files.
36#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
37#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
38#[cfg_attr(feature = "serde", serde(tag = "kind", content = "mode"))]
39#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
40pub enum AccessKind {
41    /// The catch-all case, to be used when the specific kind of event is unknown.
42    Any,
43
44    /// An event emitted when the file is read.
45    Read,
46
47    /// An event emitted when the file, or a handle to the file, is opened.
48    Open(AccessMode),
49
50    /// An event emitted when the file, or a handle to the file, is closed.
51    Close(AccessMode),
52
53    /// An event which specific kind is known but cannot be represented otherwise.
54    Other,
55}
56
57/// An event describing creation operations on files.
58#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
59#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
60#[cfg_attr(feature = "serde", serde(tag = "kind"))]
61#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
62pub enum CreateKind {
63    /// The catch-all case, to be used when the specific kind of event is unknown.
64    Any,
65
66    /// An event which results in the creation of a file.
67    File,
68
69    /// An event which results in the creation of a folder.
70    Folder,
71
72    /// An event which specific kind is known but cannot be represented otherwise.
73    Other,
74}
75
76/// An event emitted when the data content of a file is changed.
77#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
78#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
79#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
80pub enum DataChange {
81    /// The catch-all case, to be used when the specific kind of event is unknown.
82    Any,
83
84    /// An event emitted when the size of the data is changed.
85    Size,
86
87    /// An event emitted when the content of the data is changed.
88    Content,
89
90    /// An event which specific kind is known but cannot be represented otherwise.
91    Other,
92}
93
94/// An event emitted when the metadata of a file or folder is changed.
95#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
96#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
97#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
98pub enum MetadataKind {
99    /// The catch-all case, to be used when the specific kind of event is unknown.
100    Any,
101
102    /// An event emitted when the access time of the file or folder is changed.
103    AccessTime,
104
105    /// An event emitted when the write or modify time of the file or folder is changed.
106    WriteTime,
107
108    /// An event emitted when the permissions of the file or folder are changed.
109    Permissions,
110
111    /// An event emitted when the ownership of the file or folder is changed.
112    Ownership,
113
114    /// An event emitted when an extended attribute of the file or folder is changed.
115    ///
116    /// If the extended attribute's name or type is known, it should be provided in the
117    /// `Info` event attribute.
118    Extended,
119
120    /// An event which specific kind is known but cannot be represented otherwise.
121    Other,
122}
123
124/// An event emitted when the name of a file or folder is changed.
125#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
126#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
127#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
128pub enum RenameMode {
129    /// The catch-all case, to be used when the specific kind of event is unknown.
130    Any,
131
132    /// An event emitted on the file or folder resulting from a rename.
133    To,
134
135    /// An event emitted on the file or folder that was renamed.
136    From,
137
138    /// A single event emitted with both the `From` and `To` paths.
139    ///
140    /// This event should be emitted when both source and target are known. The paths should be
141    /// provided in this exact order (from, to).
142    Both,
143
144    /// An event which specific kind is known but cannot be represented otherwise.
145    Other,
146}
147
148/// An event describing mutation of content, name, or metadata.
149#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
150#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
151#[cfg_attr(feature = "serde", serde(tag = "kind", content = "mode"))]
152#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
153pub enum ModifyKind {
154    /// The catch-all case, to be used when the specific kind of event is unknown.
155    Any,
156
157    /// An event emitted when the data content of a file is changed.
158    Data(DataChange),
159
160    /// An event emitted when the metadata of a file or folder is changed.
161    Metadata(MetadataKind),
162
163    /// An event emitted when the name of a file or folder is changed.
164    #[cfg_attr(feature = "serde", serde(rename = "rename"))]
165    Name(RenameMode),
166
167    /// An event which specific kind is known but cannot be represented otherwise.
168    Other,
169}
170
171/// An event describing removal operations on files.
172#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
173#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
174#[cfg_attr(feature = "serde", serde(tag = "kind"))]
175#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
176pub enum RemoveKind {
177    /// The catch-all case, to be used when the specific kind of event is unknown.
178    Any,
179
180    /// An event emitted when a file is removed.
181    File,
182
183    /// An event emitted when a folder is removed.
184    Folder,
185
186    /// An event which specific kind is known but cannot be represented otherwise.
187    Other,
188}
189
190/// Top-level event kind.
191///
192/// This is arguably the most important classification for events. All subkinds below this one
193/// represent details that may or may not be available for any particular backend, but most tools
194/// and Notify systems will only care about which of these four general kinds an event is about.
195#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
196#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
197#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
198#[cfg_attr(
199    all(feature = "serde", not(feature = "serialization-compat-6")),
200    serde(tag = "type")
201)]
202pub enum EventKind {
203    /// The catch-all event kind, for unsupported/unknown events.
204    ///
205    /// This variant should be used as the "else" case when mapping native kernel bitmasks or
206    /// bitmaps, such that if the mask is ever extended with new event types the backend will not
207    /// gain bugs due to not matching new unknown event types.
208    ///
209    /// This variant is also the default variant used when Notify is in "imprecise" mode.
210    #[default]
211    Any,
212
213    /// An event describing non-mutating access operations on files.
214    ///
215    /// This event is about opening and closing file handles, as well as executing files, and any
216    /// other such event that is about accessing files, folders, or other structures rather than
217    /// mutating them.
218    ///
219    /// Only some platforms are capable of generating these.
220    Access(AccessKind),
221
222    /// An event describing creation operations on files.
223    ///
224    /// This event is about the creation of files, folders, or other structures but not about e.g.
225    /// writing new content into them.
226    Create(CreateKind),
227
228    /// An event describing mutation of content, name, or metadata.
229    ///
230    /// This event is about the mutation of files', folders', or other structures' content, name
231    /// (path), or associated metadata (attributes).
232    Modify(ModifyKind),
233
234    /// An event describing removal operations on files.
235    ///
236    /// This event is about the removal of files, folders, or other structures but not e.g. erasing
237    /// content from them. This may also be triggered for renames/moves that move files _out of the
238    /// watched subpath_.
239    ///
240    /// Some editors also trigger Remove events when saving files as they may opt for removing (or
241    /// renaming) the original then creating a new file in-place.
242    Remove(RemoveKind),
243
244    /// An event not fitting in any of the above four categories.
245    ///
246    /// This may be used for meta-events about the watch itself.
247    Other,
248}
249
250impl EventKind {
251    /// Indicates whether an event is an Access variant.
252    pub fn is_access(&self) -> bool {
253        matches!(self, EventKind::Access(_))
254    }
255
256    /// Indicates whether an event is a Create variant.
257    pub fn is_create(&self) -> bool {
258        matches!(self, EventKind::Create(_))
259    }
260
261    /// Indicates whether an event is a Modify variant.
262    pub fn is_modify(&self) -> bool {
263        matches!(self, EventKind::Modify(_))
264    }
265
266    /// Indicates whether an event is a Remove variant.
267    pub fn is_remove(&self) -> bool {
268        matches!(self, EventKind::Remove(_))
269    }
270
271    /// Indicates whether an event is an Other variant.
272    pub fn is_other(&self) -> bool {
273        matches!(self, EventKind::Other)
274    }
275}
276
277bitflags! {
278    /// A bitmask specifying which event kinds to monitor.
279    ///
280    /// This type allows fine-grained control over which filesystem events are reported.
281    /// On backends that support kernel-level filtering (inotify), the mask is
282    /// translated to native flags for optimal performance. On other backends (kqueue,
283    /// Windows, FSEvents, PollWatcher), filtering is applied in userspace.
284    ///
285    /// # Examples
286    ///
287    /// ```
288    /// use notify_types::event::EventKindMask;
289    ///
290    /// // Monitor only file creations and deletions
291    /// let mask = EventKindMask::CREATE | EventKindMask::REMOVE;
292    ///
293    /// // Monitor everything including access events
294    /// let all = EventKindMask::ALL;
295    ///
296    /// // Default: includes all events (matches Config::default())
297    /// let default = EventKindMask::default();
298    /// assert_eq!(default, EventKindMask::ALL);
299    /// ```
300    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
301    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
302    pub struct EventKindMask: u32 {
303        /// Monitor file/folder creation events.
304        const CREATE = 0b0000_0001;
305
306        /// Monitor file/folder removal events.
307        const REMOVE = 0b0000_0010;
308
309        /// Monitor data modification events (content/size changes).
310        const MODIFY_DATA = 0b0000_0100;
311
312        /// Monitor metadata modification events (permissions, timestamps, etc).
313        const MODIFY_META = 0b0000_1000;
314
315        /// Monitor name/rename events.
316        const MODIFY_NAME = 0b0001_0000;
317
318        /// Monitor file open events.
319        const ACCESS_OPEN = 0b0010_0000;
320
321        /// Monitor file close events after writing.
322        /// This fires when a file opened for writing is closed.
323        const ACCESS_CLOSE = 0b0100_0000;
324
325        /// Monitor file close events after read-only access.
326        /// This fires when a file opened for reading (not writing) is closed.
327        /// Note: This can be very noisy and may cause queue overflow on busy systems.
328        const ACCESS_CLOSE_NOWRITE = 0b1000_0000;
329
330        /// All modify events (data, metadata, and name changes).
331        const ALL_MODIFY = Self::MODIFY_DATA.bits() | Self::MODIFY_META.bits() | Self::MODIFY_NAME.bits();
332
333        /// All access events (open, close-write, and close-nowrite).
334        const ALL_ACCESS = Self::ACCESS_OPEN.bits() | Self::ACCESS_CLOSE.bits() | Self::ACCESS_CLOSE_NOWRITE.bits();
335
336        /// Core events: create, remove, and all modify events.
337        /// This is the default and matches the current notify behavior (no access events).
338        const CORE = Self::CREATE.bits() | Self::REMOVE.bits() | Self::ALL_MODIFY.bits();
339
340        /// All events including access events.
341        const ALL = Self::CORE.bits() | Self::ALL_ACCESS.bits();
342    }
343}
344
345impl Default for EventKindMask {
346    fn default() -> Self {
347        EventKindMask::ALL
348    }
349}
350
351impl EventKindMask {
352    /// Returns whether the given event kind matches this mask.
353    ///
354    /// `EventKind::Any` and `EventKind::Other` always pass regardless of the mask,
355    /// as they represent meta-events that should not be filtered.
356    ///
357    /// # Examples
358    ///
359    /// ```
360    /// use notify_types::event::{EventKindMask, EventKind, CreateKind, AccessKind, AccessMode};
361    ///
362    /// let mask = EventKindMask::CREATE;
363    /// assert!(mask.matches(&EventKind::Create(CreateKind::File)));
364    /// assert!(!mask.matches(&EventKind::Access(AccessKind::Open(AccessMode::Read))));
365    ///
366    /// // Any and Other always pass
367    /// let empty = EventKindMask::empty();
368    /// assert!(empty.matches(&EventKind::Any));
369    /// assert!(empty.matches(&EventKind::Other));
370    /// ```
371    pub fn matches(&self, kind: &EventKind) -> bool {
372        match kind {
373            // Meta-events always pass
374            EventKind::Any | EventKind::Other => true,
375
376            // Create events
377            EventKind::Create(_) => self.intersects(EventKindMask::CREATE),
378
379            // Remove events
380            EventKind::Remove(_) => self.intersects(EventKindMask::REMOVE),
381
382            // Modify events - check subkind
383            EventKind::Modify(modify_kind) => match modify_kind {
384                ModifyKind::Data(_) => self.intersects(EventKindMask::MODIFY_DATA),
385                ModifyKind::Metadata(_) => self.intersects(EventKindMask::MODIFY_META),
386                ModifyKind::Name(_) => self.intersects(EventKindMask::MODIFY_NAME),
387                // ModifyKind::Any and ModifyKind::Other pass if any modify flag is set
388                ModifyKind::Any | ModifyKind::Other => self.intersects(EventKindMask::ALL_MODIFY),
389            },
390
391            // Access events - check subkind
392            EventKind::Access(access_kind) => match access_kind {
393                AccessKind::Open(_) => self.intersects(EventKindMask::ACCESS_OPEN),
394                // Close after write
395                AccessKind::Close(AccessMode::Write) => {
396                    self.intersects(EventKindMask::ACCESS_CLOSE)
397                }
398                // Close after read-only (no write)
399                AccessKind::Close(AccessMode::Read) => {
400                    self.intersects(EventKindMask::ACCESS_CLOSE_NOWRITE)
401                }
402                // Close with unknown mode - match if either close flag is set
403                AccessKind::Close(_) => self
404                    .intersects(EventKindMask::ACCESS_CLOSE | EventKindMask::ACCESS_CLOSE_NOWRITE),
405                // AccessKind::Read, Any, and Other pass if any access flag is set
406                AccessKind::Read | AccessKind::Any | AccessKind::Other => {
407                    self.intersects(EventKindMask::ALL_ACCESS)
408                }
409            },
410        }
411    }
412}
413
414/// Notify event.
415///
416/// You might want to check [`Event::need_rescan`] to make sure no event was missed before you
417/// received this one.
418#[derive(Clone)]
419#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
420pub struct Event {
421    /// Kind or type of the event.
422    ///
423    /// This is a hierarchy of enums describing the event as precisely as possible. All enums in
424    /// the hierarchy have two variants always present, `Any` and `Other`, accompanied by one or
425    /// more specific variants.
426    ///
427    /// `Any` should be used when more detail about the event is not known beyond the variant
428    /// already selected. For example, `AccessMode::Any` means a file has been accessed, but that's
429    /// all we know.
430    ///
431    /// `Other` should be used when more detail _is_ available, but cannot be encoded as one of the
432    /// defined variants. When specifying `Other`, the event attributes should contain an `Info`
433    /// entry with a short string identifying this detail. That string is to be considered part of
434    /// the interface of the backend (i.e. a change should probably be breaking).
435    ///
436    /// For example, `CreateKind::Other` with an `Info("mount")` may indicate the binding of a
437    /// mount. The documentation of the particular backend should indicate if any `Other` events
438    /// are generated, and what their description means.
439    ///
440    /// The `EventKind::Any` variant should be used as the "else" case when mapping native kernel
441    /// bitmasks or bitmaps, such that if the mask is ever extended with new event types the
442    /// backend will not gain bugs due to not matching new unknown event types.
443    #[cfg_attr(
444        all(feature = "serde", not(feature = "serialization-compat-6")),
445        serde(flatten)
446    )]
447    #[cfg_attr(
448        all(feature = "serde", feature = "serialization-compat-6"),
449        serde(rename = "type")
450    )]
451    pub kind: EventKind,
452
453    /// Paths the event is about, if known.
454    ///
455    /// If an event concerns two or more paths, and the paths are known at the time of event
456    /// creation, they should all go in this `Vec`. Otherwise, using the `Tracker` attr may be more
457    /// appropriate.
458    ///
459    /// The order of the paths is likely to be significant! For example, renames where both ends of
460    /// the name change are known will have the "source" path first, and the "target" path last.
461    pub paths: Vec<PathBuf>,
462
463    // "What should be in the struct" and "what can go in the attrs" is an interesting question.
464    //
465    // Technically, the paths could go in the attrs. That would reduce the type size to 4 pointer
466    // widths, instead of 7 like it is now. Anything 8 and below is probably good — on x64 that's
467    // the size of an L1 cache line. The entire kind classification fits in 3 bytes, and an AnyMap
468    // is 3 pointers. A Vec<PathBuf> is another 3 pointers.
469    //
470    // Type size aside, what's behind these structures? A Vec and a PathBuf is stored on the heap.
471    // An AnyMap is stored on the heap. But a Vec is directly there, requiring about one access to
472    // get, while retrieving anything in the AnyMap requires some accesses as overhead.
473    //
474    // So things that are used often should be on the struct, and things that are used more rarely
475    // should go in the attrs. Additionally, arbitrary data can _only_ go in the attrs.
476    //
477    // The kind and the paths vie for first place on this scale, depending on how downstream wishes
478    // to use the information. Everything else is secondary. So far, that's why paths live here.
479    //
480    // In the future, it might be possible to have more data and to benchmark things properly, so
481    // the performance can be actually quantified. Also, it might turn out that I have no idea what
482    // I was talking about, so the above may be discarded or reviewed. We'll see!
483    //
484    /// Additional attributes of the event.
485    ///
486    /// Arbitrary data may be added to this field, without restriction beyond the `Sync` and
487    /// `Clone` properties. Some data added here is considered for comparing and hashing, but not
488    /// all: at this writing this is `Tracker`, `Flag`, `Info`, and `Source`.
489    #[cfg_attr(feature = "serde", serde(default))]
490    pub attrs: EventAttributes,
491}
492
493/// Additional attributes of the event.
494#[derive(Clone, Default, Debug)]
495#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
496pub struct EventAttributes {
497    #[cfg_attr(feature = "serde", serde(flatten))]
498    inner: Option<Box<EventAttributesInner>>,
499}
500
501#[derive(Clone, Default, Debug)]
502#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
503struct EventAttributesInner {
504    /// Tracking ID for events that are related.
505    ///
506    /// For events generated by backends with the `TrackRelated` capability. Those backends _may_
507    /// emit events that are related to each other, and tag those with an identical "tracking id"
508    /// or "cookie". The value is normalised to `usize`.
509    #[cfg_attr(
510        feature = "serde",
511        serde(default, skip_serializing_if = "Option::is_none")
512    )]
513    tracker: Option<usize>,
514
515    /// Special Notify flag on the event.
516    #[cfg_attr(
517        feature = "serde",
518        serde(default, skip_serializing_if = "Option::is_none")
519    )]
520    flag: Option<Flag>,
521
522    /// Additional information on the event.
523    ///
524    /// This is to be used for all `Other` variants of the event kind hierarchy. The variant
525    /// indicates that a consumer should look into the `attrs` for an `Info` value; if that value
526    /// is missing it should be considered a backend bug.
527    ///
528    /// This attribute may also be present for non-`Other` variants of the event kind, if doing so
529    /// provides useful precision. For example, the `Modify(Metadata(Extended))` kind suggests
530    /// using this attribute when information about _what_ extended metadata changed is available.
531    ///
532    /// This should be a short string, and changes may be considered breaking.
533    #[cfg_attr(
534        feature = "serde",
535        serde(default, skip_serializing_if = "Option::is_none")
536    )]
537    info: Option<String>,
538
539    /// The source of the event.
540    ///
541    /// In most cases this should be a short string, identifying the backend unambiguously. In some
542    /// cases this may be dynamically generated, but should contain a prefix to make it unambiguous
543    /// between backends.
544    #[cfg_attr(
545        feature = "serde",
546        serde(default, skip_serializing_if = "Option::is_none")
547    )]
548    source: Option<String>,
549
550    /// The process ID of the originator of the event.
551    ///
552    /// This attribute is experimental and, while included in Notify itself, is not considered
553    /// stable or standard enough to be part of the serde, eq, hash, and debug representations.
554    #[cfg_attr(
555        feature = "serde",
556        serde(default, skip_serializing, skip_deserializing)
557    )]
558    process_id: Option<u32>,
559}
560
561impl EventAttributes {
562    /// Creates a new `EventAttributes`.
563    pub fn new() -> Self {
564        Self { inner: None }
565    }
566
567    /// Retrieves the tracker ID for an event directly, if present.
568    pub fn tracker(&self) -> Option<usize> {
569        self.inner.as_ref().and_then(|inner| inner.tracker)
570    }
571
572    /// Retrieves the Notify flag for an event directly, if present.
573    pub fn flag(&self) -> Option<Flag> {
574        self.inner.as_ref().and_then(|inner| inner.flag)
575    }
576
577    /// Retrieves the additional info for an event directly, if present.
578    pub fn info(&self) -> Option<&str> {
579        self.inner.as_ref().and_then(|inner| inner.info.as_deref())
580    }
581
582    /// Retrieves the source for an event directly, if present.
583    pub fn source(&self) -> Option<&str> {
584        self.inner
585            .as_ref()
586            .and_then(|inner| inner.source.as_deref())
587    }
588
589    /// The process ID of the originator of the event.
590    ///
591    /// This attribute is experimental and, while included in Notify itself, is not considered
592    /// stable or standard enough to be part of the serde, eq, hash, and debug representations.
593    pub fn process_id(&self) -> Option<u32> {
594        self.inner.as_ref().and_then(|inner| inner.process_id)
595    }
596
597    /// Sets the tracker.
598    pub fn set_tracker(&mut self, tracker: usize) {
599        self.inner_mut().tracker = Some(tracker);
600    }
601
602    /// Sets the Notify flag onto the event.
603    pub fn set_flag(&mut self, flag: Flag) {
604        self.inner_mut().flag = Some(flag);
605    }
606
607    /// Sets additional info onto the event.
608    pub fn set_info(&mut self, info: &str) {
609        self.inner_mut().info = Some(info.to_string());
610    }
611
612    /// Sets the process id onto the event.
613    pub fn set_process_id(&mut self, process_id: u32) {
614        self.inner_mut().process_id = Some(process_id)
615    }
616
617    fn inner_mut(&mut self) -> &mut EventAttributesInner {
618        self.inner.get_or_insert_with(Box::default)
619    }
620}
621
622/// Special Notify flag on the event.
623///
624/// This attribute is used to flag certain kinds of events that Notify either marks or generates in
625/// particular ways.
626#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
627#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
628#[cfg_attr(
629    all(feature = "serde", not(feature = "serialization-compat-6")),
630    serde(rename_all = "camelCase")
631)]
632pub enum Flag {
633    /// Rescan notices are emitted by some platforms (and may also be emitted by Notify itself).
634    /// They indicate either a lapse in the events or a change in the filesystem such that events
635    /// received so far can no longer be relied on to represent the state of the filesystem now.
636    ///
637    /// An application that simply reacts to file changes may not care about this. An application
638    /// that keeps an in-memory representation of the filesystem will need to care, and will need
639    /// to refresh that representation directly from the filesystem.
640    Rescan,
641}
642
643impl Event {
644    /// Returns whether some events may have been missed. If true, you should assume any file or
645    /// folder might have been modified.
646    ///
647    /// See [`Flag::Rescan`] for more information.
648    pub fn need_rescan(&self) -> bool {
649        matches!(self.flag(), Some(Flag::Rescan))
650    }
651    /// Retrieves the tracker ID for an event directly, if present.
652    pub fn tracker(&self) -> Option<usize> {
653        self.attrs.tracker()
654    }
655
656    /// Retrieves the Notify flag for an event directly, if present.
657    pub fn flag(&self) -> Option<Flag> {
658        self.attrs.flag()
659    }
660
661    /// Retrieves the additional info for an event directly, if present.
662    pub fn info(&self) -> Option<&str> {
663        self.attrs.info()
664    }
665
666    /// Retrieves the source for an event directly, if present.
667    pub fn source(&self) -> Option<&str> {
668        self.attrs.source()
669    }
670
671    /// Creates a new `Event` given a kind.
672    pub fn new(kind: EventKind) -> Self {
673        Self {
674            kind,
675            paths: Vec::new(),
676            attrs: EventAttributes::new(),
677        }
678    }
679
680    /// Sets the kind.
681    pub fn set_kind(mut self, kind: EventKind) -> Self {
682        self.kind = kind;
683        self
684    }
685
686    /// Adds a path to the event.
687    pub fn add_path(mut self, path: PathBuf) -> Self {
688        self.paths.push(path);
689        self
690    }
691
692    /// Adds a path to the event if the argument is Some.
693    pub fn add_some_path(self, path: Option<PathBuf>) -> Self {
694        if let Some(path) = path {
695            self.add_path(path)
696        } else {
697            self
698        }
699    }
700
701    /// Sets the tracker.
702    pub fn set_tracker(mut self, tracker: usize) -> Self {
703        self.attrs.set_tracker(tracker);
704        self
705    }
706
707    /// Sets additional info onto the event.
708    pub fn set_info(mut self, info: &str) -> Self {
709        self.attrs.set_info(info);
710        self
711    }
712
713    /// Sets the Notify flag onto the event.
714    pub fn set_flag(mut self, flag: Flag) -> Self {
715        self.attrs.set_flag(flag);
716        self
717    }
718
719    /// Sets the process id onto the event.
720    pub fn set_process_id(mut self, process_id: u32) -> Self {
721        self.attrs.set_process_id(process_id);
722        self
723    }
724}
725
726impl fmt::Debug for Event {
727    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
728        f.debug_struct("Event")
729            .field("kind", &self.kind)
730            .field("paths", &self.paths)
731            .field("attr:tracker", &self.tracker())
732            .field("attr:flag", &self.flag())
733            .field("attr:info", &self.info())
734            .field("attr:source", &self.source())
735            .finish()
736    }
737}
738impl Default for Event {
739    fn default() -> Self {
740        Self {
741            kind: EventKind::default(),
742            paths: Vec::new(),
743            attrs: EventAttributes::new(),
744        }
745    }
746}
747
748impl Eq for Event {}
749impl PartialEq for Event {
750    fn eq(&self, other: &Self) -> bool {
751        self.kind.eq(&other.kind)
752            && self.paths.eq(&other.paths)
753            && self.tracker().eq(&other.tracker())
754            && self.flag().eq(&other.flag())
755            && self.info().eq(&other.info())
756            && self.source().eq(&other.source())
757    }
758}
759
760impl Hash for Event {
761    fn hash<H: Hasher>(&self, state: &mut H) {
762        self.kind.hash(state);
763        self.paths.hash(state);
764        self.tracker().hash(state);
765        self.flag().hash(state);
766        self.info().hash(state);
767        self.source().hash(state);
768    }
769}
770
771#[cfg(test)]
772mod event_kind_mask_tests {
773    use super::*;
774
775    #[test]
776    fn default_is_all() {
777        assert_eq!(EventKindMask::default(), EventKindMask::ALL);
778    }
779
780    #[test]
781    fn matches_create_events() {
782        let mask = EventKindMask::CREATE;
783        assert!(mask.matches(&EventKind::Create(CreateKind::File)));
784        assert!(mask.matches(&EventKind::Create(CreateKind::Folder)));
785        assert!(mask.matches(&EventKind::Create(CreateKind::Any)));
786        assert!(mask.matches(&EventKind::Create(CreateKind::Other)));
787        assert!(!mask.matches(&EventKind::Remove(RemoveKind::File)));
788    }
789
790    #[test]
791    fn matches_remove_events() {
792        let mask = EventKindMask::REMOVE;
793        assert!(mask.matches(&EventKind::Remove(RemoveKind::File)));
794        assert!(mask.matches(&EventKind::Remove(RemoveKind::Folder)));
795        assert!(mask.matches(&EventKind::Remove(RemoveKind::Any)));
796        assert!(!mask.matches(&EventKind::Create(CreateKind::File)));
797    }
798
799    #[test]
800    fn matches_access_open_events() {
801        let mask = EventKindMask::ACCESS_OPEN;
802        assert!(mask.matches(&EventKind::Access(AccessKind::Open(AccessMode::Any))));
803        assert!(mask.matches(&EventKind::Access(AccessKind::Open(AccessMode::Read))));
804        assert!(mask.matches(&EventKind::Access(AccessKind::Open(AccessMode::Write))));
805        assert!(!mask.matches(&EventKind::Access(AccessKind::Close(AccessMode::Write))));
806        assert!(!mask.matches(&EventKind::Create(CreateKind::File)));
807    }
808
809    #[test]
810    fn matches_access_close_events() {
811        // ACCESS_CLOSE only matches Close(Write), not Close(Read)
812        let mask = EventKindMask::ACCESS_CLOSE;
813        assert!(mask.matches(&EventKind::Access(AccessKind::Close(AccessMode::Write))));
814        assert!(mask.matches(&EventKind::Access(AccessKind::Close(AccessMode::Any)))); // Any could be write
815        assert!(!mask.matches(&EventKind::Access(AccessKind::Close(AccessMode::Read)))); // Read goes to NOWRITE
816        assert!(!mask.matches(&EventKind::Access(AccessKind::Open(AccessMode::Any))));
817    }
818
819    #[test]
820    fn matches_access_close_nowrite_events() {
821        // ACCESS_CLOSE_NOWRITE matches Close(Read) - files opened read-only
822        let mask = EventKindMask::ACCESS_CLOSE_NOWRITE;
823        assert!(mask.matches(&EventKind::Access(AccessKind::Close(AccessMode::Read))));
824        assert!(mask.matches(&EventKind::Access(AccessKind::Close(AccessMode::Any)))); // Any could be read
825        assert!(!mask.matches(&EventKind::Access(AccessKind::Close(AccessMode::Write)))); // Write goes to ACCESS_CLOSE
826        assert!(!mask.matches(&EventKind::Access(AccessKind::Open(AccessMode::Any))));
827    }
828
829    #[test]
830    fn combined_close_masks_match_both() {
831        // When both ACCESS_CLOSE and ACCESS_CLOSE_NOWRITE are set, match both
832        let mask = EventKindMask::ACCESS_CLOSE | EventKindMask::ACCESS_CLOSE_NOWRITE;
833        assert!(mask.matches(&EventKind::Access(AccessKind::Close(AccessMode::Write))));
834        assert!(mask.matches(&EventKind::Access(AccessKind::Close(AccessMode::Read))));
835        assert!(mask.matches(&EventKind::Access(AccessKind::Close(AccessMode::Any))));
836    }
837
838    #[test]
839    fn all_access_matches_open_close_read_any_other() {
840        let mask = EventKindMask::ALL_ACCESS;
841        assert!(mask.matches(&EventKind::Access(AccessKind::Open(AccessMode::Read))));
842        assert!(mask.matches(&EventKind::Access(AccessKind::Close(AccessMode::Write))));
843        assert!(mask.matches(&EventKind::Access(AccessKind::Read)));
844        assert!(mask.matches(&EventKind::Access(AccessKind::Any)));
845        assert!(mask.matches(&EventKind::Access(AccessKind::Other)));
846    }
847
848    #[test]
849    fn matches_modify_data_events() {
850        let mask = EventKindMask::MODIFY_DATA;
851        assert!(mask.matches(&EventKind::Modify(ModifyKind::Data(DataChange::Any))));
852        assert!(mask.matches(&EventKind::Modify(ModifyKind::Data(DataChange::Size))));
853        assert!(mask.matches(&EventKind::Modify(ModifyKind::Data(DataChange::Content))));
854        assert!(!mask.matches(&EventKind::Modify(ModifyKind::Metadata(MetadataKind::Any))));
855        assert!(!mask.matches(&EventKind::Modify(ModifyKind::Name(RenameMode::From))));
856    }
857
858    #[test]
859    fn matches_modify_metadata_events() {
860        let mask = EventKindMask::MODIFY_META;
861        assert!(mask.matches(&EventKind::Modify(ModifyKind::Metadata(MetadataKind::Any))));
862        assert!(mask.matches(&EventKind::Modify(ModifyKind::Metadata(
863            MetadataKind::Permissions
864        ))));
865        assert!(!mask.matches(&EventKind::Modify(ModifyKind::Data(DataChange::Any))));
866    }
867
868    #[test]
869    fn matches_modify_name_events() {
870        let mask = EventKindMask::MODIFY_NAME;
871        assert!(mask.matches(&EventKind::Modify(ModifyKind::Name(RenameMode::From))));
872        assert!(mask.matches(&EventKind::Modify(ModifyKind::Name(RenameMode::To))));
873        assert!(mask.matches(&EventKind::Modify(ModifyKind::Name(RenameMode::Both))));
874        assert!(!mask.matches(&EventKind::Modify(ModifyKind::Data(DataChange::Any))));
875    }
876
877    #[test]
878    fn all_modify_matches_data_meta_name() {
879        let mask = EventKindMask::ALL_MODIFY;
880        assert!(mask.matches(&EventKind::Modify(ModifyKind::Data(DataChange::Any))));
881        assert!(mask.matches(&EventKind::Modify(ModifyKind::Metadata(MetadataKind::Any))));
882        assert!(mask.matches(&EventKind::Modify(ModifyKind::Name(RenameMode::From))));
883        assert!(mask.matches(&EventKind::Modify(ModifyKind::Any)));
884        assert!(mask.matches(&EventKind::Modify(ModifyKind::Other)));
885    }
886
887    #[test]
888    fn any_and_other_always_pass() {
889        let empty = EventKindMask::empty();
890        assert!(empty.matches(&EventKind::Any));
891        assert!(empty.matches(&EventKind::Other));
892    }
893
894    #[test]
895    fn core_excludes_access() {
896        let core = EventKindMask::CORE;
897        assert!(core.matches(&EventKind::Create(CreateKind::File)));
898        assert!(core.matches(&EventKind::Remove(RemoveKind::File)));
899        assert!(core.matches(&EventKind::Modify(ModifyKind::Data(DataChange::Any))));
900        assert!(core.matches(&EventKind::Modify(ModifyKind::Metadata(MetadataKind::Any))));
901        assert!(core.matches(&EventKind::Modify(ModifyKind::Name(RenameMode::From))));
902        assert!(!core.matches(&EventKind::Access(AccessKind::Open(AccessMode::Any))));
903        assert!(!core.matches(&EventKind::Access(AccessKind::Close(AccessMode::Write))));
904    }
905
906    #[test]
907    fn empty_mask_only_passes_any_other() {
908        let empty = EventKindMask::empty();
909        assert!(empty.matches(&EventKind::Any));
910        assert!(empty.matches(&EventKind::Other));
911        assert!(!empty.matches(&EventKind::Create(CreateKind::File)));
912        assert!(!empty.matches(&EventKind::Remove(RemoveKind::File)));
913        assert!(!empty.matches(&EventKind::Modify(ModifyKind::Data(DataChange::Any))));
914        assert!(!empty.matches(&EventKind::Access(AccessKind::Open(AccessMode::Any))));
915    }
916
917    #[test]
918    fn bitwise_or_combines_masks() {
919        let mask = EventKindMask::CREATE | EventKindMask::REMOVE;
920        assert!(mask.matches(&EventKind::Create(CreateKind::File)));
921        assert!(mask.matches(&EventKind::Remove(RemoveKind::Folder)));
922        assert!(!mask.matches(&EventKind::Modify(ModifyKind::Data(DataChange::Any))));
923        assert!(!mask.matches(&EventKind::Access(AccessKind::Open(AccessMode::Any))));
924    }
925
926    #[test]
927    fn all_matches_everything() {
928        let all = EventKindMask::ALL;
929        assert!(all.matches(&EventKind::Any));
930        assert!(all.matches(&EventKind::Other));
931        assert!(all.matches(&EventKind::Create(CreateKind::File)));
932        assert!(all.matches(&EventKind::Remove(RemoveKind::File)));
933        assert!(all.matches(&EventKind::Modify(ModifyKind::Data(DataChange::Any))));
934        assert!(all.matches(&EventKind::Access(AccessKind::Open(AccessMode::Any))));
935        assert!(all.matches(&EventKind::Access(AccessKind::Close(AccessMode::Write))));
936    }
937
938    #[test]
939    fn access_read_and_any_match_with_all_access() {
940        // Edge case: AccessKind::Read and AccessKind::Any should match if ALL_ACCESS is set
941        let mask = EventKindMask::ALL_ACCESS;
942        assert!(mask.matches(&EventKind::Access(AccessKind::Read)));
943        assert!(mask.matches(&EventKind::Access(AccessKind::Any)));
944        assert!(mask.matches(&EventKind::Access(AccessKind::Other)));
945    }
946}
947
948#[cfg(all(test, feature = "serde", not(feature = "serialization-compat-6")))]
949mod tests {
950    use super::*;
951
952    use insta::assert_snapshot;
953    use rstest::rstest;
954
955    #[rustfmt::skip]
956    #[rstest]
957    #[case("any", EventKind::Any)]
958    #[case("access-any", EventKind::Access(AccessKind::Any))]
959    #[case("access-read", EventKind::Access(AccessKind::Read))]
960    #[case("access-open-any", EventKind::Access(AccessKind::Open(AccessMode::Any)))]
961    #[case("access-open-execute", EventKind::Access(AccessKind::Open(AccessMode::Execute)))]
962    #[case("access-open-read", EventKind::Access(AccessKind::Open(AccessMode::Read)))]
963    #[case("access-open-write", EventKind::Access(AccessKind::Open(AccessMode::Write)))]
964    #[case("access-open-other", EventKind::Access(AccessKind::Open(AccessMode::Other)))]
965    #[case("access-close-any", EventKind::Access(AccessKind::Close(AccessMode::Any)))]
966    #[case("access-close-execute", EventKind::Access(AccessKind::Close(AccessMode::Execute)))]
967    #[case("access-close-read", EventKind::Access(AccessKind::Close(AccessMode::Read)))]
968    #[case("access-close-write", EventKind::Access(AccessKind::Close(AccessMode::Write)))]
969    #[case("access-close-other", EventKind::Access(AccessKind::Close(AccessMode::Other)))]
970    #[case("access-other", EventKind::Access(AccessKind::Other))]
971    #[case("create-any", EventKind::Create(CreateKind::Any))]
972    #[case("create-file", EventKind::Create(CreateKind::File))]
973    #[case("create-folder", EventKind::Create(CreateKind::Folder))]
974    #[case("create-other", EventKind::Create(CreateKind::Other))]
975    #[case("modify-any", EventKind::Modify(ModifyKind::Any))]
976    #[case("modify-data-any", EventKind::Modify(ModifyKind::Data(DataChange::Any)))]
977    #[case("modify-data-size", EventKind::Modify(ModifyKind::Data(DataChange::Size)))]
978    #[case("modify-data-content", EventKind::Modify(ModifyKind::Data(DataChange::Content)))]
979    #[case("modify-data-other", EventKind::Modify(ModifyKind::Data(DataChange::Other)))]
980    #[case("modify-metadata-any", EventKind::Modify(ModifyKind::Metadata(MetadataKind::Any)))]
981    #[case("modify-metadata-accesstime", EventKind::Modify(ModifyKind::Metadata(MetadataKind::AccessTime)))]
982    #[case("modify-metadata-writetime", EventKind::Modify(ModifyKind::Metadata(MetadataKind::WriteTime)))]
983    #[case("modify-metadata-permissions", EventKind::Modify(ModifyKind::Metadata(MetadataKind::Permissions)))]
984    #[case("modify-metadata-ownership", EventKind::Modify(ModifyKind::Metadata(MetadataKind::Ownership)))]
985    #[case("modify-metadata-extended", EventKind::Modify(ModifyKind::Metadata(MetadataKind::Extended)))]
986    #[case("modify-metadata-other", EventKind::Modify(ModifyKind::Metadata(MetadataKind::Other)))]
987    #[case("modify-name-any", EventKind::Modify(ModifyKind::Name(RenameMode::Any)))]
988    #[case("modify-name-to", EventKind::Modify(ModifyKind::Name(RenameMode::To)))]
989    #[case("modify-name-from", EventKind::Modify(ModifyKind::Name(RenameMode::From)))]
990    #[case("modify-name-both", EventKind::Modify(ModifyKind::Name(RenameMode::Both)))]
991    #[case("modify-name-other", EventKind::Modify(ModifyKind::Name(RenameMode::Other)))]
992    #[case("modify-other", EventKind::Modify(ModifyKind::Other))]
993    #[case("remove-any", EventKind::Remove(RemoveKind::Any))]
994    #[case("remove-file", EventKind::Remove(RemoveKind::File))]
995    #[case("remove-folder", EventKind::Remove(RemoveKind::Folder))]
996    #[case("remove-other", EventKind::Remove(RemoveKind::Other))]
997    #[case("other", EventKind::Other)]
998    fn serialize_event_kind(
999        #[case] name: &str,
1000        #[case] event_kind: EventKind,
1001    ) {
1002        let event = Event::new(event_kind);
1003        let json = serde_json::to_string(&event).unwrap();
1004        assert_snapshot!(name, json);
1005    }
1006
1007    #[test]
1008    fn serialize_event_with_attrs() {
1009        let event = Event::new(EventKind::Any)
1010            .set_tracker(123)
1011            .set_flag(Flag::Rescan)
1012            .set_info("test event")
1013            .set_process_id(0);
1014        let json = serde_json::to_string(&event).unwrap();
1015        assert_snapshot!(json);
1016    }
1017}