| 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 | |