1 | //! The `Event` type and the hierarchical `EventKind` descriptor. |
2 | |
3 | use std::{ |
4 | fmt, |
5 | hash::{Hash, Hasher}, |
6 | path::PathBuf, |
7 | }; |
8 | |
9 | #[cfg (feature = "serde" )] |
10 | use 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" ))] |
16 | pub 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" ))] |
38 | pub 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" ))] |
60 | pub 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" ))] |
78 | pub 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" ))] |
96 | pub 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" ))] |
126 | pub 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" ))] |
151 | pub 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" ))] |
174 | pub 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 | )] |
200 | pub 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 | |
248 | impl 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))] |
281 | pub 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))] |
357 | pub 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))] |
364 | struct 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 | |
422 | impl 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 | )] |
493 | pub 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 | |
504 | impl 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 | |
587 | impl fmt::Debug for Event { |
588 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
589 | f&mut DebugStruct<'_, '_>.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(name:"attr:source" , &self.source()) |
596 | .finish() |
597 | } |
598 | } |
599 | impl 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 | |
609 | impl Eq for Event {} |
610 | impl 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 | |
621 | impl 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" )))] |
633 | mod 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 | } |
702 | |