watchexec 2.0.2

Library to execute commands in response to file modifications
Documentation

Crates.io page API Docs Crate license: Apache 2.0 MSRV: 1.60.0 (minor) CI status

Watchexec library

The library which powers Watchexec CLI and other tools.

  • API documentation.
  • Licensed under Apache 2.0.
  • Minimum Supported Rust Version: 1.60.0 (incurs a minor semver bump).
  • Status: maintained.

Quick start

use miette::{IntoDiagnostic, Result};
use watchexec::{
    Watchexec,
    action::{Action, Outcome},
    config::{InitConfig, RuntimeConfig},
    handler::{Handler as _, PrintDebug},
};

#[tokio::main]
async fn main() -> Result<()> {
    let mut init = InitConfig::default();
    init.on_error(PrintDebug(std::io::stderr()));

    let mut runtime = RuntimeConfig::default();
    runtime.pathset(["watchexec.conf"]);

    let conf = YourConfigFormat::load_from_file("watchexec.conf").await.into_diagnostic()?;
    conf.apply(&mut runtime);

    let we = Watchexec::new(init, runtime.clone())?;
    let w = we.clone();

    let c = runtime.clone();
    runtime.on_action(move |action: Action| {
        let mut c = c.clone();
        let w = w.clone();
        async move {
            for event in action.events.iter() {
                if event.paths().any(|(p, _)| p.ends_with("/watchexec.conf")) {
                    let conf = YourConfigFormat::load_from_file("watchexec.conf").await?;

                    conf.apply(&mut c);
                    let _ = w.reconfigure(c.clone());
                    // tada! self-reconfiguring watchexec on config file change!

                    break;
                }
            }

            action.outcome(Outcome::if_running(
                Outcome::DoNothing,
                Outcome::both(Outcome::Clear, Outcome::Start),
            ));

            Ok(())

            // (not normally required! ignore this when implementing)
            as std::result::Result<_, MietteStub>
        }
    });

    we.reconfigure(runtime);
    we.main().await.into_diagnostic()?;
    Ok(())
}

// ignore this! it's stuff to make the above code get checked by cargo doc tests!
struct YourConfigFormat; impl YourConfigFormat { async fn load_from_file(_: &str) -> std::result::Result<Self, MietteStub> { Ok(Self) } fn apply(&self, _: &mut RuntimeConfig) {} } use miette::Diagnostic; use thiserror::Error; #[derive(Debug, Error, Diagnostic)] #[error("stub")] struct MietteStub;

Kitchen sink

The library also exposes a number of components which are available to make your own tool, or to make anything else you may want:

  • Command handling, to build a command with an arbitrary shell, deal with grouped and ungrouped processes the same way, and supervise a process while also listening for & acting on interventions such as sending signals.

  • Event sources: Filesystem, Signals, (more to come).

  • Finding a common prefix of a set of paths.

  • And more!

Filterers are split into their own crates, so they can be evolved independently:

  • The Globset filterer implements the default Watchexec filter, and mimics the pre-1.18 behaviour as much as possible.

  • The Tagged filterer is an experiment in creating a more powerful filtering solution, which can operate on every part of events, not just their paths.

  • The Ignore filterer implements ignore-file semantics, and especially supports trees of ignore files. It is used as a subfilterer in both of the main filterers above.

There are also separate, standalone crates used to build Watchexec which you can tap into:

  • ClearScreen makes clearing the terminal screen in a cross-platform way easy by default, and provides advanced options to fit your usecase.

  • Command Group augments the std and tokio Command with support for process groups, portable between Unix and Windows.

  • Ignore files finds, parses, and interprets ignore files.

  • Project Origins finds the origin (or root) path of a project, and what kind of project it is.