Documentation
¶
Overview ¶
Package watcher provides recursive filesystem watching with debouncing for blog post directories.
It wraps github.com/fsnotify/fsnotify with automatic recursive directory watching (fsnotify only watches individual directories non-recursively) and a configurable debounce window that coalesces rapid bursts of events — such as those produced by editors that write files in multiple steps — into a single callback invocation.
Basic Usage ¶
Create a Watcher rooted at a posts directory and run it alongside a pkg/server.Server to achieve live content reload:
import (
"context"
"log/slog"
"os"
"github.com/harrydayexe/GoBlog/v2/pkg/server"
"github.com/harrydayexe/GoBlog/v2/pkg/watcher"
)
postsPath := "posts/"
w, err := watcher.New(postsPath)
if err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// srv is a *server.Server created with server.New(...)
go w.Run(ctx, func(ctx context.Context) {
if err := srv.UpdatePosts(os.DirFS(postsPath), ctx); err != nil {
slog.Warn("failed to reload posts", "error", err)
}
})
// srv.Run blocks until the server shuts down.
if err := srv.Run(ctx); err != nil {
log.Fatal(err)
}
Setup Errors ¶
New fails hard on any problem detected at setup time: root path missing or not a directory, fsnotify initialisation failure, or any individual directory failing to be added to the watch list (e.g. due to exceeding the Linux inotify watch limit). These are surfaced immediately so the user is aware that watching is not working as requested.
Runtime Behaviour ¶
Once Run is active, errors are logged at warn level and the loop continues. This includes fsnotify errors and failures adding newly created subdirectories to the watch set.
Subdirectories created after the Watcher is constructed are automatically picked up by Run when the parent directory fires a Create event.
Only changes to files with a .md extension trigger the onChange callback. All other file types (images, CSS, YAML, etc.) are silently ignored, as are common editor temporary files (dotfiles, *.swp, *~, etc.). Deletion of watched subdirectories releases the corresponding watch descriptor automatically. A subdirectory that is removed and then recreated is re-watched when the parent fires the subsequent Create event.
Concurrency ¶
New and Run are not safe for concurrent use on the same Watcher. Typically a single Watcher is created once and Run in a dedicated goroutine.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Watcher ¶
type Watcher struct {
config.WatcherDebounce
// contains filtered or unexported fields
}
Watcher watches a directory tree for filesystem changes and invokes a callback, debounced, whenever changes are detected.
func New ¶
func New(path string, opts ...config.WatcherOption) (*Watcher, error)
New creates a Watcher rooted at path. It recursively watches path and all subdirectories that exist at creation time. Subdirectories created after New returns are picked up automatically inside Run when their parent fires a Create event.
New fails immediately if any part of setup fails — including the root path being missing, fsnotify initialisation failing, or any subdirectory failing to be added to the watch list. Callers should not attempt to fall back silently; surface the error to the user.
Call Run to begin receiving events.
func (*Watcher) Run ¶
Run blocks, watching for filesystem events, until ctx is canceled. onChange is invoked (with a child context) whenever file changes are detected, coalesced over the configured debounce window. Multiple events within the window trigger only one onChange call.
Errors from the fsnotify event stream are logged at warn level but do not stop the watch loop. Run returns nil when ctx is canceled.