Module fs/watch

fs/watch
Stability: unstable — shipped in v0.2.20 and still unstable at v0.2.24. This is a deliberate extension of the one-release window, not drift: the Windows backend is not delivered, and the shape of Windows change delivery (`ReadDirectoryChangesW` batches renames as paired events) is the one thing most likely to move `Change`. macOS and Linux are usable today. Freezing follows Windows delivery, not a release count. — stable modules only change additively; this one may still change.

File-system change notifications (fs.watch, plans/archive/STD_API_AUDIT.md §7 P1).

A Watcher observes one path — a directory (entries created, removed, renamed or written) or a single file — through the runtime's native backend (inotify on Linux, kqueue on macOS, ReadDirectoryChangesW on Windows; the std/sys/events layer). Events are delivered on the event loop and queued inside the watcher; read them with the non-blocking poll() or the awaitable next(io). next is the Stream trait's method (std/async/stream.yo), so a watcher composes: w.filter(…).map(…).take(n).

{ watch, WatchOptions, FsEventKind } :: import("std/fs/watch");
w := watch(Path.new(`./logs`), WatchOptions.defaults(), exn);
ev := io.await(w.next(io), io);          // Option(FsEvent) — .None once closed
match(ev, .Some(e) => println(`${e.name} ${e.kind}`), .None => ());
w.close();

close() is idempotent and runs automatically when the last reference to the watcher goes away, so a watcher that simply falls out of scope stops its native watch rather than leaving the event loop calling back into freed memory.

Event kinds follow the backend's (and libuv's) two-way split: Rename covers create / delete / move, Change covers a write or metadata change. name is the entry name relative to a watched directory (the file's own name when a file is watched) on every platform — on macOS the runtime diffs a snapshot of the directory and reports each changed entry; an event about the directory itself (or a vanished root) carries an empty name. Platforms coalesce differently, so a single write may surface as one or several Changes — treat the stream as "something happened", not as an exact journal.

Stability

unstable — shipped in v0.2.20 and still unstable at v0.2.24. This is a deliberate extension of the one-release window, not drift: the Windows backend is not delivered, and the shape of Windows change delivery (ReadDirectoryChangesW batches renames as paired events) is the one thing most likely to move Change. macOS and Linux are usable today. Freezing follows Windows delivery, not a release count.

Types

FsEventKind

What happened to the entry.

Variants

VariantFieldsDescription
Rename

Created, removed or moved (the backend's "rename" class).

Change

Contents or metadata changed.

Trait Implementations

impl(generic(T : Type), where(T <: ToString), T : (ToString))
impl(generic(T : Type), where(T <: ToString), T : (ToString), Format)
format : fn(self : Self, spec : str) -> String

Render self under spec. An unrecognised spec degrades to the plain to_string() rendering rather than failing.

Parameters

NameTypeNotes
selfSelf
specstr

Returns: String

impl(FsEventKind, ToString(...))
to_string : ( self -> match( self, .Rename => String.from("rename"), .Change => String.from("change") ) )
Methods
to_string : (FsEventKind) fn(self : FsEventKind) -> String

Render self as the text a USER should read — Rust's Display::fmt, not its Debug. Hand-written (or generated by derive(Error) from a per-variant format string) whenever the structural form would be wrong.

Parameters

NameTypeNotes
selfFsEventKind

Returns: String

FsEvent struct
FsEvent

One notification.

Fields

NameTypeDescription
nameString

Entry name relative to the watched directory (or the watched file's own name); empty when the backend reports no name.

kindFsEventKind

Which of the backend's two classes this was. Platforms coalesce differently, so a Rename says "created, removed or moved" rather than which of the three.

WatchOptions struct
WatchOptions

Watch settings.

Fields

NameTypeDescription
recursivebool

Watch the whole tree below a directory. Native on macOS/Windows; on Linux inotify is per directory, so this is best-effort there.

impl(WatchOptions, ...)
defaults : (WatchOptions) fn() -> WatchOptions

Watch only the named directory itself — recursive : false. That is the conservative default because recursive is best-effort on Linux (inotify is per directory), so a program written against the default behaves the same on every platform.

Returns: WatchOptions

Watcher object
Watcher

A live watch. Create with watch, drain with poll/next, and close when done — closing stops the native watch and makes next resolve .None for any waiter.

Fields

NameTypeDescription
_handleFsEventHandle
_rootPath
_queueArrayList(FsEvent)
_headusize

Index of the first unread event in _queue; the list is compacted whenever it has been fully drained.

_activebool

Trait Implementations

impl(generic(S : Type), where(S <: Stream), S : (Stream))
map : fn(generic(A, B, F) self : S : (Stream), f : F : (Fn(A) -> B)) -> StreamMap(S : (Stream), B, F : (Fn(A) -> B))

Parameters

NameTypeNotes
selfS : (Stream)
fF : (Fn(A) -> B)

Returns: StreamMap(S : (Stream), B, F : (Fn(A) -> B))

filter : fn(generic(A, F) self : S : (Stream), f : F : (Fn(A) -> bool)) -> StreamFilter(S : (Stream), F : (Fn(A) -> bool))

Parameters

NameTypeNotes
selfS : (Stream)
fF : (Fn(A) -> bool)

Returns: StreamFilter(S : (Stream), F : (Fn(A) -> bool))

filter_map : fn(generic(A, B, F) self : S : (Stream), f : F : (Fn(A) -> Option(B))) -> StreamFilterMap(S : (Stream), B, F : (Fn(A) -> Option(B)))

Parameters

NameTypeNotes
selfS : (Stream)
fF : (Fn(A) -> Option(B))

Returns: StreamFilterMap(S : (Stream), B, F : (Fn(A) -> Option(B)))

take : fn(generic(A) self : S : (Stream), n : usize) -> StreamTake(S : (Stream))

Parameters

NameTypeNotes
selfS : (Stream)
nusize

Returns: StreamTake(S : (Stream))

skip : fn(generic(A) self : S : (Stream), n : usize) -> StreamSkip(S : (Stream))

Parameters

NameTypeNotes
selfS : (Stream)
nusize

Returns: StreamSkip(S : (Stream))

for_each : fn(generic(A, F) self : S : (Stream), f : F : (Fn(A) -> unit), io : Io) -> Impl : (Future[Future](unit) Io : Io)

Parameters

NameTypeNotes
selfS : (Stream)
fF : (Fn(A) -> unit)
ioIo

Returns: Impl : (Future[Future](unit) Io : Io)

collect : fn(generic(A) self : S : (Stream), io : Io) -> Impl : (Future[Future](ArrayList(A)) Io : Io)

Parameters

NameTypeNotes
selfS : (Stream)
ioIo

Returns: Impl : (Future[Future](ArrayList(A)) Io : Io)

impl(Watcher, ...)
path : (Watcher) fn(self : Watcher) -> Path

The watched path.

Parameters

NameTypeNotes
selfWatcher

Returns: Path

is_active : (Watcher) fn(self : Watcher) -> bool

Whether the watch is still running.

Parameters

NameTypeNotes
selfWatcher

Returns: bool

pending : (Watcher) fn(self : Watcher) -> usize

Events queued and not yet read.

Parameters

NameTypeNotes
selfWatcher

Returns: usize

poll : (Watcher) fn(self : Watcher) -> Option(FsEvent)

Take the next queued event without waiting; .None when the queue is empty.

Parameters

NameTypeNotes
selfWatcher

Returns: Option(FsEvent)

close : (Watcher) fn(self : Watcher) -> unit

Stop the native watch. Queued events stay readable through poll; next no longer waits. Idempotent, and called for you when the last reference to the watcher goes away (Dispose below).

Parameters

NameTypeNotes
selfWatcher

Returns: unit

impl(Watcher, Stream(...))
Item : FsEvent
next : (Watcher) fn(self : Watcher, io : Io) -> Impl : (Future[Future](Option(FsEvent)) Io : Io)

Wait for the next event. Resolves .None once the watcher is closed and its queue drained. Suspends through yield, so it is safe inside tasks (no nested event loop).

Parameters

NameTypeNotes
selfWatcher
ioIo

Returns: Impl : (Future[Future](Option(FsEvent)) Io : Io)

impl(Watcher, Dispose(...))
dispose : (Watcher) fn(self : Watcher) -> unit

Release the resources self owns — a file descriptor, a socket, a lock, a buffer the allocator handed out. Called automatically when the last reference to the value goes away, so an implementor never calls it directly and must tolerate being the only one who ever does.

It must be safe to run exactly once: the runtime calls it at refcount zero, and a type that also exposes an explicit close/release is responsible for making the second call a no-op.

Parameters

NameTypeNotes
selfWatcher

Returns: unit

Functions

watch function
fn(path : Path, opts : WatchOptions, exn : Exception) -> Watcher

Start watching path. Throws the backend's IoError (e.g. NotFound) through exn.

Parameters

NameTypeNotesDescription
pathPath

The watched path.

optsWatchOptions
exnException

Returns: Watcher