#watcher #filesystem #watchexec

watchexec

Library to execute commands in response to file modifications

59 releases (stable)

new 2.2.0 Mar 18, 2023
2.1.0 Jan 9, 2023
2.0.2 Sep 7, 2022
2.0.0 Jun 17, 2022
1.5.0 Nov 23, 2016

#18 in Filesystem

Download history 4170/week @ 2022-11-28 4315/week @ 2022-12-05 4313/week @ 2022-12-12 3684/week @ 2022-12-19 3619/week @ 2022-12-26 4110/week @ 2023-01-02 5299/week @ 2023-01-09 4244/week @ 2023-01-16 5262/week @ 2023-01-23 5353/week @ 2023-01-30 5087/week @ 2023-02-06 6816/week @ 2023-02-13 5265/week @ 2023-02-20 4959/week @ 2023-02-27 5478/week @ 2023-03-06 4563/week @ 2023-03-13

21,056 downloads per month
Used in 30 crates (15 directly)

Apache-2.0

205KB
4.5K SLoC

Crates.io page API Docs Crate license: Apache 2.0 CI status

Watchexec library

The library which powers Watchexec CLI and other tools.

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, Keyboard, (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.

  • Event types contains the event types used by Watchexec, including the JSON format used for passing event data to child processes.

  • Signal types contains the signal types used by Watchexec.

  • 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.

Rust version (MSRV)

Due to the unpredictability of dependencies changing their MSRV, this library no longer tries to keep to a minimum supported Rust version behind stable. Instead, it is assumed that developers use the latest stable at all times.

Applications that wish to support lower-than-stable Rust (such as the Watchexec CLI does) should:

  • use a lock file
  • recommend the use of --locked when installing from source
  • provide pre-built binaries (and Binstall support) for non-distro users
  • avoid using newer features until some time has passed, to let distro users catch up
  • consider recommending that distro-Rust users switch to distro rustup where available

Dependencies

~12–44MB
~761K SLoC