hotwatch

repository·main·Indexed 40 days ago

https://github.com/francesca64/hotwatch

A lightweight Rust library (v0.5.0) that provides a simplified wrapper around the notify crate for monitoring file system changes via callbacks. It supports both blocking and non-blocking instances, event debouncing, and recursive directory watching.

Tokens
1.8K
Snippets
8
Records
10
Agent score
50%

What's inside hotwatch

  1. Quickstart with hotwatch

    main

    To use hotwatch, initialize a new Hotwatch instance and use the .watch() method to register a callback for a specific file path. The callback receives an Event object, which you can inspect using event.kind to determine the type of change (e.g., EventKind::Modify).

    Note: Only the latest stable version of Rust is supported.

    use hotwatch::{Hotwatch, Event, EventKind};
    
    let mut hotwatch = Hotwatch::new().expect("hotwatch failed to initialize!");
    hotwatch.watch("war.png", |event: Event| {
        if let EventKind::Modify(_) = event.kind {
            println!("War has changed.");
        }
    }).expect("failed to watch file!");
  2. How Hotwatch threading and handlers work

    main

    Hotwatch operates using a background thread model to ensure file watching does not block your main application execution:

    1. Background Thread: When Hotwatch is initialized, a dedicated thread is spawned to listen for file system events.
    2. Event Debouncing: Events are debounced based on the duration provided during initialization.
    3. Handler Execution: When an event occurs, the background thread identifies the most specific registered handler for that path and executes the closure.
    4. Concurrency Warning: Because handlers run on the background thread, they must be Send. If you need to interact with the rest of your application, you must use synchronization primitives like Arc<Mutex<T>> to share state safely between the background thread and your main thread.
  3. Initialize a blocking Hotwatch instance

    main

    Use Hotwatch::new() to create a new instance with a default debounce delay of 2 seconds. Alternatively, use Hotwatch::new_with_custom_delay(delay: std::time::Duration) to specify a custom debounce interval.

    Note: On macOS, setting a delay greater than 30 seconds may prevent the repetition of previous events.

    Dropping the Hotwatch instance will automatically unwatch all registered paths.

    use hotwatch::blocking::Hotwatch;
    use std::time::Duration;
    
    // Default 2s delay
    let hotwatch = Hotwatch::new().expect("hotwatch failed to initialize");
    
    // Custom delay
    let hotwatch = Hotwatch::new_with_custom_delay(Duration::from_secs(5)).expect("hotwatch failed to initialize");
  4. Initialize a non-blocking Hotwatch instance

    main

    Use Hotwatch::new() to create a new instance with a default debounce delay of 2 seconds. This starts a background thread that handles file watching and callback execution. Dropping the Hotwatch instance will automatically stop all watching.

    Alternatively, use Hotwatch::new_with_custom_delay(delay: Duration) to specify a custom debounce interval.

    Note: On macOS, setting a delay greater than 30 seconds may prevent the repetition of previous events.

    use hotwatch::Hotwatch;
    
    // Default 2-second delay
    let hotwatch = Hotwatch::new().expect("hotwatch failed to initialize");
  5. Stop watching a path

    main

    Use unwatch(path) to stop monitoring a specific path and remove its associated handler. This will fail if the path was not being watched or if the underlying platform-specific unwatch operation fails.

    // Assuming hotwatch is already initialized
    hotwatch.unwatch("README.md").expect("failed to unwatch");
  6. Watch a path and register a handler

    main

    Register a callback for a specific path using watch(path, handler).

    • Path Specificity: If you watch both a directory and a file within it, only the most specific handler (the file handler) will be called for changes to that file.
    • Recursion: When watching a directory, the handler will receive events for all contents, including subdirectories.
    • Threading: Handlers are executed on the background watch thread. If your handler needs to access data from the main thread, you must use thread-safe primitives (like Arc<Mutex<T>>) and you will likely need to use the move keyword in your closure.
    • Errors: Returns Error::Io if the path cannot be canonicalized or read.
    use hotwatch::{Hotwatch, Event, EventKind};
    
    let mut hotwatch = Hotwatch::new().expect("hotwatch failed to initialize!");
    
    hotwatch.watch("README.md", |event: Event| {
        if let EventKind::Modify(_) = event.kind {
            println!("{:?} changed!", event.paths[0]);
        }
    }).expect("failed to watch file!");
  7. Run the blocking event loop

    main

    Call run() to start the event loop. This method blocks the current thread and waits for file system events.

    • The loop continues indefinitely as long as handlers return Flow::Continue.
    • The loop terminates if any handler returns Flow::Exit.
    • If the internal communication channel is closed, the loop terminates.
    let mut hotwatch = Hotwatch::new().unwrap();
    hotwatch.watch("src", |event| {
        println!("Event: {:?}", event);
        Flow::Continue
    }).unwrap();
    
    // This blocks the thread
    hotwatch.run();
  8. Handle Hotwatch errors

    main

    The Error enum encapsulates errors from both I/O operations and the underlying notify crate. It implements std::error::Error and std::fmt::Display.

    Possible variants:

    • Io(std::io::Error): Occurs during path canonicalization or file access.
    • Notify(notify::Error): Occurs when the underlying file watcher fails.
    #[derive(Debug)]
    pub enum Error {
        Io(std::io::Error),
        Notify(notify::Error),
    }
  9. Control the event loop with Flow

    main

    The Flow enum determines the lifecycle of the blocking Hotwatch::run() loop within a handler.

    • Flow::Continue: The loop continues watching and blocks the thread for the next event.
    • Flow::Exit: The loop stops watching and returns control of the thread to the caller.
    use hotwatch::blocking::Flow;
    
    // Inside a handler:
    if condition_met {
        Flow::Exit
    } else {
        Flow::Continue
    }