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
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12/// An event describing open or close operations on files.
13#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
14#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
15#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
16pub enum AccessMode {
17 /// The catch-all case, to be used when the specific kind of event is unknown.
18 Any,
19
20 /// An event emitted when the file is executed, or the folder opened.
21 Execute,
22
23 /// An event emitted when the file is opened for reading.
24 Read,
25
26 /// An event emitted when the file is opened for writing.
27 Write,
28
29 /// An event which specific kind is known but cannot be represented otherwise.
30 Other,
31}
32
33/// An event describing non-mutating access operations on files.
34#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36#[cfg_attr(feature = "serde", serde(tag = "kind", content = "mode"))]
37#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
38pub enum AccessKind {
39 /// The catch-all case, to be used when the specific kind of event is unknown.
40 Any,
41
42 /// An event emitted when the file is read.
43 Read,
44
45 /// An event emitted when the file, or a handle to the file, is opened.
46 Open(AccessMode),
47
48 /// An event emitted when the file, or a handle to the file, is closed.
49 Close(AccessMode),
50
51 /// An event which specific kind is known but cannot be represented otherwise.
52 Other,
53}
54
55/// An event describing creation operations on files.
56#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
57#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
58#[cfg_attr(feature = "serde", serde(tag = "kind"))]
59#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
60pub enum CreateKind {
61 /// The catch-all case, to be used when the specific kind of event is unknown.
62 Any,
63
64 /// An event which results in the creation of a file.
65 File,
66
67 /// An event which results in the creation of a folder.
68 Folder,
69
70 /// An event which specific kind is known but cannot be represented otherwise.
71 Other,
72}
73
74/// An event emitted when the data content of a file is changed.
75#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
76#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
77#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
78pub enum DataChange {
79 /// The catch-all case, to be used when the specific kind of event is unknown.
80 Any,
81
82 /// An event emitted when the size of the data is changed.
83 Size,
84
85 /// An event emitted when the content of the data is changed.
86 Content,
87
88 /// An event which specific kind is known but cannot be represented otherwise.
89 Other,
90}
91
92/// An event emitted when the metadata of a file or folder is changed.
93#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
94#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
95#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
96pub enum MetadataKind {
97 /// The catch-all case, to be used when the specific kind of event is unknown.
98 Any,
99
100 /// An event emitted when the access time of the file or folder is changed.
101 AccessTime,
102
103 /// An event emitted when the write or modify time of the file or folder is changed.
104 WriteTime,
105
106 /// An event emitted when the permissions of the file or folder are changed.
107 Permissions,
108
109 /// An event emitted when the ownership of the file or folder is changed.
110 Ownership,
111
112 /// An event emitted when an extended attribute of the file or folder is changed.
113 ///
114 /// If the extended attribute's name or type is known, it should be provided in the
115 /// `Info` event attribute.
116 Extended,
117
118 /// An event which specific kind is known but cannot be represented otherwise.
119 Other,
120}
121
122/// An event emitted when the name of a file or folder is changed.
123#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
124#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
125#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
126pub enum RenameMode {
127 /// The catch-all case, to be used when the specific kind of event is unknown.
128 Any,
129
130 /// An event emitted on the file or folder resulting from a rename.
131 To,
132
133 /// An event emitted on the file or folder that was renamed.
134 From,
135
136 /// A single event emitted with both the `From` and `To` paths.
137 ///
138 /// This event should be emitted when both source and target are known. The paths should be
139 /// provided in this exact order (from, to).
140 Both,
141
142 /// An event which specific kind is known but cannot be represented otherwise.
143 Other,
144}
145
146/// An event describing mutation of content, name, or metadata.
147#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
148#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
149#[cfg_attr(feature = "serde", serde(tag = "kind", content = "mode"))]
150#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
151pub enum ModifyKind {
152 /// The catch-all case, to be used when the specific kind of event is unknown.
153 Any,
154
155 /// An event emitted when the data content of a file is changed.
156 Data(DataChange),
157
158 /// An event emitted when the metadata of a file or folder is changed.
159 Metadata(MetadataKind),
160
161 /// An event emitted when the name of a file or folder is changed.
162 #[cfg_attr(feature = "serde", serde(rename = "rename"))]
163 Name(RenameMode),
164
165 /// An event which specific kind is known but cannot be represented otherwise.
166 Other,
167}
168
169/// An event describing removal operations on files.
170#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
171#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
172#[cfg_attr(feature = "serde", serde(tag = "kind"))]
173#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
174pub enum RemoveKind {
175 /// The catch-all case, to be used when the specific kind of event is unknown.
176 Any,
177
178 /// An event emitted when a file is removed.
179 File,
180
181 /// An event emitted when a folder is removed.
182 Folder,
183
184 /// An event which specific kind is known but cannot be represented otherwise.
185 Other,
186}
187
188/// Top-level event kind.
189///
190/// This is arguably the most important classification for events. All subkinds below this one
191/// represent details that may or may not be available for any particular backend, but most tools
192/// and Notify systems will only care about which of these four general kinds an event is about.
193#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
194#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
195#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
196#[cfg_attr(
197 all(feature = "serde", not(feature = "serialization-compat-6")),
198 serde(tag = "type")
199)]
200pub enum EventKind {
201 /// The catch-all event kind, for unsupported/unknown events.
202 ///
203 /// This variant should be used as the "else" case when mapping native kernel bitmasks or
204 /// bitmaps, such that if the mask is ever extended with new event types the backend will not
205 /// gain bugs due to not matching new unknown event types.
206 ///
207 /// This variant is also the default variant used when Notify is in "imprecise" mode.
208 #[default]
209 Any,
210
211 /// An event describing non-mutating access operations on files.
212 ///
213 /// This event is about opening and closing file handles, as well as executing files, and any
214 /// other such event that is about accessing files, folders, or other structures rather than
215 /// mutating them.
216 ///
217 /// Only some platforms are capable of generating these.
218 Access(AccessKind),
219
220 /// An event describing creation operations on files.
221 ///
222 /// This event is about the creation of files, folders, or other structures but not about e.g.
223 /// writing new content into them.
224 Create(CreateKind),
225
226 /// An event describing mutation of content, name, or metadata.
227 ///
228 /// This event is about the mutation of files', folders', or other structures' content, name
229 /// (path), or associated metadata (attributes).
230 Modify(ModifyKind),
231
232 /// An event describing removal operations on files.
233 ///
234 /// This event is about the removal of files, folders, or other structures but not e.g. erasing
235 /// content from them. This may also be triggered for renames/moves that move files _out of the
236 /// watched subpath_.
237 ///
238 /// Some editors also trigger Remove events when saving files as they may opt for removing (or
239 /// renaming) the original then creating a new file in-place.
240 Remove(RemoveKind),
241
242 /// An event not fitting in any of the above four categories.
243 ///
244 /// This may be used for meta-events about the watch itself.
245 Other,
246}
247
248impl EventKind {
249 /// Indicates whether an event is an Access variant.
250 pub fn is_access(&self) -> bool {
251 matches!(self, EventKind::Access(_))
252 }
253
254 /// Indicates whether an event is a Create variant.
255 pub fn is_create(&self) -> bool {
256 matches!(self, EventKind::Create(_))
257 }
258
259 /// Indicates whether an event is a Modify variant.
260 pub fn is_modify(&self) -> bool {
261 matches!(self, EventKind::Modify(_))
262 }
263
264 /// Indicates whether an event is a Remove variant.
265 pub fn is_remove(&self) -> bool {
266 matches!(self, EventKind::Remove(_))
267 }
268
269 /// Indicates whether an event is an Other variant.
270 pub fn is_other(&self) -> bool {
271 matches!(self, EventKind::Other)
272 }
273}
274
275/// Notify event.
276///
277/// You might want to check [`Event::need_rescan`] to make sure no event was missed before you
278/// received this one.
279#[derive(Clone)]
280#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
281pub struct Event {
282 /// Kind or type of the event.
283 ///
284 /// This is a hierarchy of enums describing the event as precisely as possible. All enums in
285 /// the hierarchy have two variants always present, `Any` and `Other`, accompanied by one or
286 /// more specific variants.
287 ///
288 /// `Any` should be used when more detail about the event is not known beyond the variant
289 /// already selected. For example, `AccessMode::Any` means a file has been accessed, but that's
290 /// all we know.
291 ///
292 /// `Other` should be used when more detail _is_ available, but cannot be encoded as one of the
293 /// defined variants. When specifying `Other`, the event attributes should contain an `Info`
294 /// entry with a short string identifying this detail. That string is to be considered part of
295 /// the interface of the backend (i.e. a change should probably be breaking).
296 ///
297 /// For example, `CreateKind::Other` with an `Info("mount")` may indicate the binding of a
298 /// mount. The documentation of the particular backend should indicate if any `Other` events
299 /// are generated, and what their description means.
300 ///
301 /// The `EventKind::Any` variant should be used as the "else" case when mapping native kernel
302 /// bitmasks or bitmaps, such that if the mask is ever extended with new event types the
303 /// backend will not gain bugs due to not matching new unknown event types.
304 #[cfg_attr(
305 all(feature = "serde", not(feature = "serialization-compat-6")),
306 serde(flatten)
307 )]
308 #[cfg_attr(
309 all(feature = "serde", feature = "serialization-compat-6"),
310 serde(rename = "type")
311 )]
312 pub kind: EventKind,
313
314 /// Paths the event is about, if known.
315 ///
316 /// If an event concerns two or more paths, and the paths are known at the time of event
317 /// creation, they should all go in this `Vec`. Otherwise, using the `Tracker` attr may be more
318 /// appropriate.
319 ///
320 /// The order of the paths is likely to be significant! For example, renames where both ends of
321 /// the name change are known will have the "source" path first, and the "target" path last.
322 pub paths: Vec<PathBuf>,
323
324 // "What should be in the struct" and "what can go in the attrs" is an interesting question.
325 //
326 // Technically, the paths could go in the attrs. That would reduce the type size to 4 pointer
327 // widths, instead of 7 like it is now. Anything 8 and below is probably good — on x64 that's
328 // the size of an L1 cache line. The entire kind classification fits in 3 bytes, and an AnyMap
329 // is 3 pointers. A Vec<PathBuf> is another 3 pointers.
330 //
331 // Type size aside, what's behind these structures? A Vec and a PathBuf is stored on the heap.
332 // An AnyMap is stored on the heap. But a Vec is directly there, requiring about one access to
333 // get, while retrieving anything in the AnyMap requires some accesses as overhead.
334 //
335 // So things that are used often should be on the struct, and things that are used more rarely
336 // should go in the attrs. Additionally, arbitrary data can _only_ go in the attrs.
337 //
338 // The kind and the paths vie for first place on this scale, depending on how downstream wishes
339 // to use the information. Everything else is secondary. So far, that's why paths live here.
340 //
341 // In the future, it might be possible to have more data and to benchmark things properly, so
342 // the performance can be actually quantified. Also, it might turn out that I have no idea what
343 // I was talking about, so the above may be discarded or reviewed. We'll see!
344 //
345 /// Additional attributes of the event.
346 ///
347 /// Arbitrary data may be added to this field, without restriction beyond the `Sync` and
348 /// `Clone` properties. Some data added here is considered for comparing and hashing, but not
349 /// all: at this writing this is `Tracker`, `Flag`, `Info`, and `Source`.
350 #[cfg_attr(feature = "serde", serde(default))]
351 pub attrs: EventAttributes,
352}
353
354/// Additional attributes of the event.
355#[derive(Clone, Default, Debug)]
356#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
357pub struct EventAttributes {
358 #[cfg_attr(feature = "serde", serde(flatten))]
359 inner: Option<Box<EventAttributesInner>>,
360}
361
362#[derive(Clone, Default, Debug)]
363#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
364struct EventAttributesInner {
365 /// Tracking ID for events that are related.
366 ///
367 /// For events generated by backends with the `TrackRelated` capability. Those backends _may_
368 /// emit events that are related to each other, and tag those with an identical "tracking id"
369 /// or "cookie". The value is normalised to `usize`.
370 #[cfg_attr(
371 feature = "serde",
372 serde(default, skip_serializing_if = "Option::is_none")
373 )]
374 tracker: Option<usize>,
375
376 /// Special Notify flag on the event.
377 #[cfg_attr(
378 feature = "serde",
379 serde(default, skip_serializing_if = "Option::is_none")
380 )]
381 flag: Option<Flag>,
382
383 /// Additional information on the event.
384 ///
385 /// This is to be used for all `Other` variants of the event kind hierarchy. The variant
386 /// indicates that a consumer should look into the `attrs` for an `Info` value; if that value
387 /// is missing it should be considered a backend bug.
388 ///
389 /// This attribute may also be present for non-`Other` variants of the event kind, if doing so
390 /// provides useful precision. For example, the `Modify(Metadata(Extended))` kind suggests
391 /// using this attribute when information about _what_ extended metadata changed is available.
392 ///
393 /// This should be a short string, and changes may be considered breaking.
394 #[cfg_attr(
395 feature = "serde",
396 serde(default, skip_serializing_if = "Option::is_none")
397 )]
398 info: Option<String>,
399
400 /// The source of the event.
401 ///
402 /// In most cases this should be a short string, identifying the backend unambiguously. In some
403 /// cases this may be dynamically generated, but should contain a prefix to make it unambiguous
404 /// between backends.
405 #[cfg_attr(
406 feature = "serde",
407 serde(default, skip_serializing_if = "Option::is_none")
408 )]
409 source: Option<String>,
410
411 /// The process ID of the originator of the event.
412 ///
413 /// This attribute is experimental and, while included in Notify itself, is not considered
414 /// stable or standard enough to be part of the serde, eq, hash, and debug representations.
415 #[cfg_attr(
416 feature = "serde",
417 serde(default, skip_serializing, skip_deserializing)
418 )]
419 process_id: Option<u32>,
420}
421
422impl EventAttributes {
423 /// Creates a new `EventAttributes`.
424 pub fn new() -> Self {
425 Self { inner: None }
426 }
427
428 /// Retrieves the tracker ID for an event directly, if present.
429 pub fn tracker(&self) -> Option<usize> {
430 self.inner.as_ref().and_then(|inner| inner.tracker)
431 }
432
433 /// Retrieves the Notify flag for an event directly, if present.
434 pub fn flag(&self) -> Option<Flag> {
435 self.inner.as_ref().and_then(|inner| inner.flag)
436 }
437
438 /// Retrieves the additional info for an event directly, if present.
439 pub fn info(&self) -> Option<&str> {
440 self.inner.as_ref().and_then(|inner| inner.info.as_deref())
441 }
442
443 /// Retrieves the source for an event directly, if present.
444 pub fn source(&self) -> Option<&str> {
445 self.inner
446 .as_ref()
447 .and_then(|inner| inner.source.as_deref())
448 }
449
450 /// The process ID of the originator of the event.
451 ///
452 /// This attribute is experimental and, while included in Notify itself, is not considered
453 /// stable or standard enough to be part of the serde, eq, hash, and debug representations.
454 pub fn process_id(&self) -> Option<u32> {
455 self.inner.as_ref().and_then(|inner| inner.process_id)
456 }
457
458 /// Sets the tracker.
459 pub fn set_tracker(&mut self, tracker: usize) {
460 self.inner_mut().tracker = Some(tracker);
461 }
462
463 /// Sets the Notify flag onto the event.
464 pub fn set_flag(&mut self, flag: Flag) {
465 self.inner_mut().flag = Some(flag);
466 }
467
468 /// Sets additional info onto the event.
469 pub fn set_info(&mut self, info: &str) {
470 self.inner_mut().info = Some(info.to_string());
471 }
472
473 /// Sets the process id onto the event.
474 pub fn set_process_id(&mut self, process_id: u32) {
475 self.inner_mut().process_id = Some(process_id)
476 }
477
478 fn inner_mut(&mut self) -> &mut EventAttributesInner {
479 self.inner.get_or_insert_with(Box::default)
480 }
481}
482
483/// Special Notify flag on the event.
484///
485/// This attribute is used to flag certain kinds of events that Notify either marks or generates in
486/// particular ways.
487#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
488#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
489#[cfg_attr(
490 all(feature = "serde", not(feature = "serialization-compat-6")),
491 serde(rename_all = "camelCase")
492)]
493pub enum Flag {
494 /// Rescan notices are emitted by some platforms (and may also be emitted by Notify itself).
495 /// They indicate either a lapse in the events or a change in the filesystem such that events
496 /// received so far can no longer be relied on to represent the state of the filesystem now.
497 ///
498 /// An application that simply reacts to file changes may not care about this. An application
499 /// that keeps an in-memory representation of the filesystem will need to care, and will need
500 /// to refresh that representation directly from the filesystem.
501 Rescan,
502}
503
504impl Event {
505 /// Returns whether some events may have been missed. If true, you should assume any file or
506 /// folder might have been modified.
507 ///
508 /// See [`Flag::Rescan`] for more information.
509 pub fn need_rescan(&self) -> bool {
510 matches!(self.flag(), Some(Flag::Rescan))
511 }
512 /// Retrieves the tracker ID for an event directly, if present.
513 pub fn tracker(&self) -> Option<usize> {
514 self.attrs.tracker()
515 }
516
517 /// Retrieves the Notify flag for an event directly, if present.
518 pub fn flag(&self) -> Option<Flag> {
519 self.attrs.flag()
520 }
521
522 /// Retrieves the additional info for an event directly, if present.
523 pub fn info(&self) -> Option<&str> {
524 self.attrs.info()
525 }
526
527 /// Retrieves the source for an event directly, if present.
528 pub fn source(&self) -> Option<&str> {
529 self.attrs.source()
530 }
531
532 /// Creates a new `Event` given a kind.
533 pub fn new(kind: EventKind) -> Self {
534 Self {
535 kind,
536 paths: Vec::new(),
537 attrs: EventAttributes::new(),
538 }
539 }
540
541 /// Sets the kind.
542 pub fn set_kind(mut self, kind: EventKind) -> Self {
543 self.kind = kind;
544 self
545 }
546
547 /// Adds a path to the event.
548 pub fn add_path(mut self, path: PathBuf) -> Self {
549 self.paths.push(path);
550 self
551 }
552
553 /// Adds a path to the event if the argument is Some.
554 pub fn add_some_path(self, path: Option<PathBuf>) -> Self {
555 if let Some(path) = path {
556 self.add_path(path)
557 } else {
558 self
559 }
560 }
561
562 /// Sets the tracker.
563 pub fn set_tracker(mut self, tracker: usize) -> Self {
564 self.attrs.set_tracker(tracker);
565 self
566 }
567
568 /// Sets additional info onto the event.
569 pub fn set_info(mut self, info: &str) -> Self {
570 self.attrs.set_info(info);
571 self
572 }
573
574 /// Sets the Notify flag onto the event.
575 pub fn set_flag(mut self, flag: Flag) -> Self {
576 self.attrs.set_flag(flag);
577 self
578 }
579
580 /// Sets the process id onto the event.
581 pub fn set_process_id(mut self, process_id: u32) -> Self {
582 self.attrs.set_process_id(process_id);
583 self
584 }
585}
586
587impl fmt::Debug for Event {
588 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
589 f.debug_struct("Event")
590 .field("kind", &self.kind)
591 .field("paths", &self.paths)
592 .field("attr:tracker", &self.tracker())
593 .field("attr:flag", &self.flag())
594 .field("attr:info", &self.info())
595 .field("attr:source", &self.source())
596 .finish()
597 }
598}
599impl Default for Event {
600 fn default() -> Self {
601 Self {
602 kind: EventKind::default(),
603 paths: Vec::new(),
604 attrs: EventAttributes::new(),
605 }
606 }
607}
608
609impl Eq for Event {}
610impl PartialEq for Event {
611 fn eq(&self, other: &Self) -> bool {
612 self.kind.eq(&other.kind)
613 && self.paths.eq(&other.paths)
614 && self.tracker().eq(&other.tracker())
615 && self.flag().eq(&other.flag())
616 && self.info().eq(&other.info())
617 && self.source().eq(&other.source())
618 }
619}
620
621impl Hash for Event {
622 fn hash<H: Hasher>(&self, state: &mut H) {
623 self.kind.hash(state);
624 self.paths.hash(state);
625 self.tracker().hash(state);
626 self.flag().hash(state);
627 self.info().hash(state);
628 self.source().hash(state);
629 }
630}
631
632#[cfg(all(test, feature = "serde", not(feature = "serialization-compat-6")))]
633mod tests {
634 use super::*;
635
636 use insta::assert_snapshot;
637 use rstest::rstest;
638
639 #[rustfmt::skip]
640 #[rstest]
641 #[case("any", EventKind::Any)]
642 #[case("access-any", EventKind::Access(AccessKind::Any))]
643 #[case("access-read", EventKind::Access(AccessKind::Read))]
644 #[case("access-open-any", EventKind::Access(AccessKind::Open(AccessMode::Any)))]
645 #[case("access-open-execute", EventKind::Access(AccessKind::Open(AccessMode::Execute)))]
646 #[case("access-open-read", EventKind::Access(AccessKind::Open(AccessMode::Read)))]
647 #[case("access-open-write", EventKind::Access(AccessKind::Open(AccessMode::Write)))]
648 #[case("access-open-other", EventKind::Access(AccessKind::Open(AccessMode::Other)))]
649 #[case("access-close-any", EventKind::Access(AccessKind::Close(AccessMode::Any)))]
650 #[case("access-close-execute", EventKind::Access(AccessKind::Close(AccessMode::Execute)))]
651 #[case("access-close-read", EventKind::Access(AccessKind::Close(AccessMode::Read)))]
652 #[case("access-close-write", EventKind::Access(AccessKind::Close(AccessMode::Write)))]
653 #[case("access-close-other", EventKind::Access(AccessKind::Close(AccessMode::Other)))]
654 #[case("access-other", EventKind::Access(AccessKind::Other))]
655 #[case("create-any", EventKind::Create(CreateKind::Any))]
656 #[case("create-file", EventKind::Create(CreateKind::File))]
657 #[case("create-folder", EventKind::Create(CreateKind::Folder))]
658 #[case("create-other", EventKind::Create(CreateKind::Other))]
659 #[case("modify-any", EventKind::Modify(ModifyKind::Any))]
660 #[case("modify-data-any", EventKind::Modify(ModifyKind::Data(DataChange::Any)))]
661 #[case("modify-data-size", EventKind::Modify(ModifyKind::Data(DataChange::Size)))]
662 #[case("modify-data-content", EventKind::Modify(ModifyKind::Data(DataChange::Content)))]
663 #[case("modify-data-other", EventKind::Modify(ModifyKind::Data(DataChange::Other)))]
664 #[case("modify-metadata-any", EventKind::Modify(ModifyKind::Metadata(MetadataKind::Any)))]
665 #[case("modify-metadata-accesstime", EventKind::Modify(ModifyKind::Metadata(MetadataKind::AccessTime)))]
666 #[case("modify-metadata-writetime", EventKind::Modify(ModifyKind::Metadata(MetadataKind::WriteTime)))]
667 #[case("modify-metadata-permissions", EventKind::Modify(ModifyKind::Metadata(MetadataKind::Permissions)))]
668 #[case("modify-metadata-ownership", EventKind::Modify(ModifyKind::Metadata(MetadataKind::Ownership)))]
669 #[case("modify-metadata-extended", EventKind::Modify(ModifyKind::Metadata(MetadataKind::Extended)))]
670 #[case("modify-metadata-other", EventKind::Modify(ModifyKind::Metadata(MetadataKind::Other)))]
671 #[case("modify-name-any", EventKind::Modify(ModifyKind::Name(RenameMode::Any)))]
672 #[case("modify-name-to", EventKind::Modify(ModifyKind::Name(RenameMode::To)))]
673 #[case("modify-name-from", EventKind::Modify(ModifyKind::Name(RenameMode::From)))]
674 #[case("modify-name-both", EventKind::Modify(ModifyKind::Name(RenameMode::Both)))]
675 #[case("modify-name-other", EventKind::Modify(ModifyKind::Name(RenameMode::Other)))]
676 #[case("modify-other", EventKind::Modify(ModifyKind::Other))]
677 #[case("remove-any", EventKind::Remove(RemoveKind::Any))]
678 #[case("remove-file", EventKind::Remove(RemoveKind::File))]
679 #[case("remove-folder", EventKind::Remove(RemoveKind::Folder))]
680 #[case("remove-other", EventKind::Remove(RemoveKind::Other))]
681 #[case("other", EventKind::Other)]
682 fn serialize_event_kind(
683 #[case] name: &str,
684 #[case] event_kind: EventKind,
685 ) {
686 let event = Event::new(event_kind);
687 let json = serde_json::to_string(&event).unwrap();
688 assert_snapshot!(name, json);
689 }
690
691 #[test]
692 fn serialize_event_with_attrs() {
693 let event = Event::new(EventKind::Any)
694 .set_tracker(123)
695 .set_flag(Flag::Rescan)
696 .set_info("test event")
697 .set_process_id(0);
698 let json = serde_json::to_string(&event).unwrap();
699 assert_snapshot!(json);
700 }
701}