-
Notifications
You must be signed in to change notification settings - Fork 229
The Event Guide
By default, Notify offers little detail on events. In the configuration guide, you can find how to turn precise events on, which makes two important changes to the way Notify delivers events:
- The kind of an event is populated with the full tree classification, described below, and
- Events are debounced based both on path and event type (as a result you may get more of them).
Notify does its best to smooth out platform differences, but event kind is one place where it cannot do that fully. The "imprecise" default behaviour is a safe view ("something changed at this path") of a messy reality. You should consider whether you need that sort of precision and whether it's worth dealing with the complicated nature of platform event classification.
Notify uses a nested, or tree-based, event classification system. At the top level, there are six main categories, two of which are special, and then every subcategory and subsub(sub...)category drills down further into what exactly the event is about.
Every level has two special categories:
-
Any
, which indicates that the event may be any of the possibilities of this level, and it is not known which; -
Other
, which indicates that the event is not any of the defined possibilities at this level, but it is known which. In that case, the event will have anInfo(String)
attribute with a short description.
The top-level Other
is even more special in that it indicates events about the watching itself, i.e. meta-events. These are described later and in most cases should either be entirely ignored or handled on a case-by-case basis. In imprecise mode, EventKind::Any
and EventKind::Other
are the only two event kinds available: Any
to indicate a filesystem change, Other
to indicate a meta change.
The event classification may change, and it is best to refer to the API docs (TODO: link), but at time of writing it looks like this (note that the Any
/Other
categories at each level are left out):
- Access
- Read
- Open
- Execute
- Read
- Write
- Close
- Execute
- Read
- Write
- Create
- File
- Folder
- Modify
- Data
- Size
- Content
- Metadata
- AccessTime
- WriteTime
- Permissions
- Ownership
- Extended
- Name
- To
- From
- Both
- Data
- Remove
- File
- Folder
It is not guaranteed that all event kinds are emitted by Notify at any point in time. Some categories were included in anticipation, some only for completeness. Furthermore, the classification can change. For these reasons, you should not attempt to write exhaustive matches. Rather, the best practice is to match as precisely as you care for, and wildcard the rest:
match event.kind {
EventKind::Modify(ModifyKind::Metadata(_)) => { /* deal with metadata */ }
EventKind::Create(CreateKind::File) => { /* deal with new files */ }
EventKind::Other(_) => { /* ignore meta events */ }
_ => { /* something else changed */ }
}
For simple cases, helpers are available to match one of the top level categories:
if event.kind.is_access() {}
if event.kind.is_create() {}
if event.kind.is_modify() {}
if event.kind.is_remove() {}
if event.kind.is_other() {}
For full detail about what each variant is meant for, refer to [the API docs].
Beyond their kind, events refer to zero or more paths (order may be significant), and have zero or more attributes. These attributes can be arbitrary data, of which some types are "well-known", and standardised in Notify. Of these, the Info
and Flag
types are relevant here:
-
Info
may contain a free-form string offering more detail on the particular event. This is especially relevant forOther
kinds. -
Flag
denotes some specific mutually-exclusive (events cannot have two flags) meta-information. This may have expanded (refer to [the API docs]!) but as of writing:-
Flag::Notice
is used for events emitted as part of theNoticeEvents
option. -
Flag::Ongoing
is used for events emitted as part of theOngoingEvents
option. -
Flag::Rescan
is used to indicate that some events may have been missed (see [details on the API docs]).
-
Note especially that an Other
event may have a Flag
and no Info
, or even nothing at all (though that is likely a bug): write your code carefully to cope.
The rest of this page is dedicated to documenting the behaviour of specific platforms in regards to event classification. If you deal with precise events, I strongly encourage you to regard this as required reading.
Within each section, behaviours are numbered to be easily referenced, and not for ordering (please do not change their order! strike out entries that need removal instead of deleting them).
What is not documented here is standard behaviour (described in [the API docs]).
Some common terminology:
- A "watch" refers to a
Watcher::watch
call, and the abstract entity that it creates, which monitors an object or subtree. - A watch is "ended" or "destroyed" either by a
Watcher::unwatch
call or an interior event. Generally, no new events related to a watch are generated once that watch is ended. - A "watched $object" refers to the object pointed to by the path passed to a
Watcher::watch
call, and not to an arbitrary object somewhere inside a watched subtree.
- There is no distinction between data writes or metadata writes. Both of these are represented by
Modify(Any)
. - If a watched directory is removed, the watch is ended.
- If a watched file is removed, the path of that file will continue to be watched until either the watch is manually ended or the parent folder is removed.
- A rename with both the source and destination paths inside a watched directory produces a
Modify(Name(From))
and aModify(Name(To))
in immediate mode, and a singleModify(Name(Both))
in debounced mode. Both events have aTracker
attribute set to the same value. - A rename with only the source inside a watched directory produces a
Remove
event. - A rename with only the destination inside a watched directory produces a
Create
event. - If a watched directory is renamed, no event is produced, but the directory continues to be watched. New events will be prefixed by the old path.
- If a watched file is renamed, a
Modify(Name)
is produced, but the old path continues to be watched. - If the parent of a watched object is renamed, the watch will continue. New events will be prefixed by the old path.
- If a watched object is removed, a
Remove
is produced, and the watch continues for that object's path. If a new object appears at that path at a latter time before the watch is manually ended, events for that object will be produced. - Any rename is produced as
Modify(Name(Any))
in immediate mode, and only some of these are caught and produced asModify(Name(Both))
in debounced mode. TheTracker
may or may not be set. - If a watched object is renamed, a
Modify(Name)
is produced, and then the behaviour is as in 1. - If the parent of a watched object is renamed, no event is produced, and then the behaviour is as in 1.
- If the platform indicates that some events were coalesced, a meta-event with the
Rescan
flag is produced.
- This platform emits some access events.
- If a watched object is removed, its watch is ended.
- A rename with both the source and destination paths inside a watched directory produces a
Modify(Name(From))
and aModify(Name(To))
in immediate mode, and a singleModify(Name(Both))
in debounced mode. Both events have aTracker
attribute set to the same value. - A rename with only the source inside a watched directory produces a
Remove
event. - A rename with only the destination inside a watched directory produces a
Create
event. - If a watched object is renamed, a
Modify(Name)
is produced, and the directory continues to be watched. New events will be prefixed by the old path. - If the parent of a watched object is renamed, the watch will continue. New events will be prefixed by the old path.
- If the platform indicates that the kernel queue overflowed, a meta-event with the
Rescan
flag is produced.