moon_fswatch

A debounced "something changed under here" signal.

moon_fswatch.watcher

const DEFAULT_DEBOUNCE_MS: int#

Default quiet period before a filesystem change is emitted.

fn hidden_path(path: string) -> bool#

Anything under a dot-directory, or a dotfile itself. Offered as a building block for an interesting predicate rather than applied automatically, since a caller watching dotfiles deliberately would be surprised.

  • path: Path to classify.
struct FileWatcher#

Debounced recursive filesystem watcher configuration.

directories: [string]

Roots to watch, each recursively.

debounce_ms: int

How long a window stays open after the last interesting event.

interesting: fn(string, string) -> bool

Called as interesting(kind, path); only a true opens or extends a window.

on_change: fn([string]) -> void

Called once per window with the paths it collected, deduplicated and in first-arrival order.

stop: Channel<int>

Anything sent here asks the running loop to stop.

task: Optional<Future<int>>

The running loop, so stop can wait for it; empty before start.

fn watcher(directories: [string], debounce_ms: int, interesting: fn(string, string) -> bool, on_change: fn([string]) -> void) -> FileWatcher#

interesting(kind, path) decides which raw events open a debounce window; on_change fires once per window with the paths that window collected, deduplicated and in the order they first arrived.

  • directories: Roots to watch, each recursively.
  • debounce_ms: How long a window stays open after the last interesting event.
  • interesting: Called as interesting(kind, path) for each raw event.
  • on_change: Called once per window with the paths it collected.
use moon_fswatch.watcher

fn main() {
  let mut files = watcher.watcher(
    ["src"],
    watcher.DEFAULT_DEBOUNCE_MS,
    |_kind, path| !watcher.hidden_path(path) && path.ends_with(".mn"),
    |paths| println("changed: " + paths.join(", ")),
  )
  files.start().unwrap()
  // Later, from an async shutdown path: `await files.stop()`.
  // `start` takes `mut self` (it records the running loop); `stop` does not.
}
impl FileWatcher#
fn start(self: FileWatcher) -> Result<int, Error>#

Starts the configured watcher loop; a second call is a no-op. The loop owns the watcher it opens and closes it when stopped.

async fn stop(self: FileWatcher) -> intasync#

Asks the loop to stop, waits for it, and returns how many invalidations it fired. A group still open at that point is delivered: shutdown is exactly when a catalog most wants invalidating. Closing the stop channel is the request, so the watcher can be stopped through any copy of it — a shutdown closure's, say — and stopping one that never started is 0.