include-exclude-watcher

Crates.ioinclude-exclude-watcher
lib.rsinclude-exclude-watcher
version0.2.0
created_at2025-11-26 11:53:31.243512+00
updated_at2026-01-04 19:19:15.171961+00
descriptionAsync file watcher with glob-based include/exclude patterns and built-in debouncing
homepage
repositoryhttps://github.com/vanviegen/include-exclude-watcher.rs
max_upload_size
id1951312
size83,876
Frank van Viegen (vanviegen)

documentation

https://docs.rs/include-exclude-watcher

README

include-exclude-watcher.rs

Async file watcher with glob-based include/exclude patterns. Linux only (inotify).

Why this crate?

Most file watchers (like notify) give you all events and let you filter afterwards. This works fine for small directories, but wastes resources on large trees when you only care about specific patterns.

This crate:

  • Exposes an easy-to-use tokio async/await API
  • Supports gitignore-style patterns out of the box
  • Only watches directories that could match your include/exclude patterns
  • Provides debouncing
  • Has minimal dependencies (libc and 2 tokio features)

Tradeoffs:

  • Linux only (uses inotify directly)
  • Simpler pattern syntax than full gitignore
  • Patterns on a running watcher cannot be modified

Installation

[dependencies]
include-exclude-watcher = "0.1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }

Usage

Basic watching

use include_exclude_watcher::{WatchBuilder, WatchEvent};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    WatchBuilder::new()
        .add_include("**/*.rs")
        .add_exclude("**/target/**")
        .run(|event, path| {
            println!("{:?}: {}", event, path.display());
        })
        .await
}

This uses the current working directory as its base directory.

With debouncing

use include_exclude_watcher::WatchBuilder;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    WatchBuilder::new()
        .set_base_dir("./src")
        .add_include("**/*.rs")
        .run_debounced(500, || {
            println!("Files changed, rebuilding...");
        })
        .await
}

Loading patterns from files

use include_exclude_watcher::WatchBuilder;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    WatchBuilder::new()
        .set_base_dir("/project")
        .add_include("**/*")
        .add_ignore_file(".gitignore")
        .add_ignore_file(".watchignore")
        .run(|event, path| {
            println!("{:?}: {}", event, path.display());
        })
        .await
}

Pattern files use gitignore syntax:

  • Lines starting with # are comments
  • Other non-empty lines are exclude patterns
  • Note: ! negation patterns are not supported (excludes always take precedence over includes)

Pattern syntax

  • * matches any characters except /
  • ** matches any characters including /
  • ? matches any single character except /
  • [abc] matches any character in the set
  • Patterns without / match anywhere (like gitignore)

Examples:

  • *.rs → matches foo.rs and src/bar.rs
  • src/*.rs → matches src/main.rs but not src/sub/lib.rs
  • **/test_*.rs → matches test files anywhere
  • target/** → excludes everything under target

CLI tool

cargo install include-exclude-watcher --features cli
iow ./src -i "**/*.rs" -e "**/target/**"

Options:

  • -i, --include <PATTERN> — Include pattern (can be repeated)
  • -e, --exclude <PATTERN> — Exclude pattern (can be repeated)
  • -p, --pattern-file <FILE> — Load patterns from file
  • -r, --run <COMMAND> — Run shell command on each event (sets $IOW_FILE and $IOW_EVENT)
  • -c, --combine <MS> — Debounce and output "CHANGES" after quiet period
  • -x, --exit — Exit after first change
  • -f, --format <FORMAT> — Output format: default, path, silent
  • -q, --quiet — Suppress status messages

Platform support

Linux only for now. Uses inotify directly. PRs for other platforms welcome.

Comparison with alternatives

Feature include-exclude-watcher notify watchexec
Pattern-aware watching
Built-in debouncing separate crate
Cross-platform
Async
Gitignore file support

License

MIT

Commit count: 0

cargo fmt